2019/11/03 (日) に開催された 富山 Ruby 会議 01 の LT で使用した資料。
RSpec 導入奮戦記2019/11/03富山 Ruby 会議 01 #toyamarkmuryoimpl
View Slide
とあるプロジェクトのお話既存のRailsアプリケーションに対し、前任者が用意したRSpeccoverage 5% の状態から、RSpec をほぼ実装したことのないメンバー4人とともに、学習しつつ、model, decorator, uploader,service のテスト, request spec, system spec を作成してリファクタリングするところまでやるぞ!という、Railsアプリの運用を楽しくしよう!とチャレンジをしている最中の物語。
ねらい● テストを作成する文化を根付かせたい● 本体コードが になっているので、リファクタリングして意図のわかるコードにしたい● 上記2つを通して、メンバーのスキルアップを図りたい
編成● メンバー 4 人○ RSpec ほぼ実戦では書いたことがない○ 伸びしろ 100%● 私○ コーチ○ テストコードのレビュー○ 難易度高いところをひたすらテスト書く係
現時点での計画● 機能のエンハンスは期間限定で停止 。 ただし、調査・修正は実施する。○ なるべくテスト作成に力を注ぐため● 本体をリファクタリングしたい欲をぐっと堪えてテスト作成に専念する 。○ 実行単位の難易度を下げる○ いろんなところに波及しがちなのでテスト揃えてからやる● 難易度を考え、model のテストから順に作成していくこととする。○ テストのフィードバックが一番速いはず○ 容易なメソッドが多かったため① ② ③当作戦は3期計画を予定している。① model/decorator/uploader テスト作成期② request spec, system spec作成期③ リファクタリング期※現在 ① 期後半戦 (作戦開始後ほぼ1ヶ月)
実践編 その1● 当作成実行前に、モブプロ形式でテーブルに対応した ActiveRecord のモデルを使ってブートキャンプを行う○ データ作成で FactoryBot を使う、それを it/specify 内で使うために let を使う○ User#full_name で “#{last_name}#{first_name}” に対して eq matcher を使う○ .find で ActiveRecord::RecordNotFound が発生することを expect { … }.to で確認する○ .find_by で be_nil を使う, allow を使って .find_by! 相当の挙動を and_raise で実現する○ #save で change matcher, valid? 呼び出しに対し have_received matcher を使う○ allow/expect は設定するobjectが検証対象のobjectと同一かどうか注意する● 実戦前に rubocop-rspec を導入しておく○ 既存のテストについては警告が大量に出るので除外し、テスト追加時に併せて対応する○ コーチが指摘する量を減らし、その時間をより本質的な指摘に使うため○ 警告食らってドキュメント見て Copの意図を理解してもらう狙いも
実践編 その2● モブプロ形式でモデルのテストを追加していき、その場でマージしていく○ 2時間/日 を確保してもらって、ドライバを替えながら 対象 VS 私たち でテストに立ち向かう○ 別件調査等でテスト作成の作業時間が確保できないメンバーもテスト作成、レビューができるように会として設定している => 特にレビューが滞りがち○ PR レビュー形式だとコーチがピンポイントで better な案を指摘しがち=> テスト作成者たちが考え、議論することを重視 し、指摘はクイズ形式、解答は意図を添えて○ モブプロ以外にも時間が確保できるメンバーは PR作成、質問をバンバンしてもらう○ 出ているPRもこの時間でみんなでレビューして指摘、マージする○ 実は他のコードをコピペしてきて解決しようとするのを抑止している● コーチはひたすら事例を作成するようにテストを量産してレビューにまわす○ 書き方のパターンを提供する○ 新たなパターンが出てきた場合は、モブプロ時に紹介する○ …ちょっと機会を奪っている気もする …
伝承編 これらのリンクを紹介し、一部説明を加えた● RSpec によるユニットテストの書き方 - recompile.net○ https://recompile.net/posts/how-to-write-unit-test-with-rspec.html● RSpecのletを使うのはどんなときか?(翻訳)○ https://qiita.com/jnchito/items/cdd9eef2ed193267c651● GETTING_STARTED.md - thoughtbot/factory_bot○ https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md● スペックのス○ https://magazine.rubyist.net/articles/0021/0021-Rspec.html○ https://magazine.rubyist.net/articles/0023/0023-Rspec.html● 改めて学ぶ RSpec○ https://magazine.rubyist.net/articles/0035/0035-RSpecInPractice.html● Everyday Rails - RSpec による Rails テスト入門○ https://leanpub.com/everydayrailsrspec-jp
よく出た注意点● 振る舞いをテストすること。振る舞いのテスト != メソッドのテスト○ carrierwave の store_dir のパス比較しても嬉しくない。動作させた上で store_dir に保存されることを確認したい● use_transactional_fixtures, DatabaseCleaner, DatabaseRewinder 等の設定から example ごとにデータが削除 or ロールバックされているか確認する○ この設定が入っているにもかかわらず、着手当初は手前の exampleで作成したデータが残っていること前提でテストを書きがち。テストの独立性の話で --order rand と一緒にお伝えしておくのがよさそう。● テスト作成をガイドするのもテスト○ テストをどう書くかを悩むときにテスト流さずに机上で悩みがち。簡単に手元でテストが流せるから、バンバン流してフィードバックもらって作っていって欲しい (願)。
はたして現状はどうなっているか (1ヶ月経過時点)● メンバーはRSpec読み書きできるレベルには到達した○ rspec-mock である処理を握りつぶしたり …を書くのはもう少し先の話○ 本体コード修正PRにもテストが付属してくるようになった○ PRレビュー時に他のメンバーからもテストについて指摘が出てくるようにもなった○ subject, let をうまく使った読みやすいテストコードが出てくるようになった● 思ったよりテスト作成の速度が出ていない○ 問い合わせによる調査、修正の時間が思っていたよりある。時間の確保が課題になった○ 時間確保のため、モブプロ時間の内容の見直しや、作業の仕方を模索中。スプリントごとに施策を替え、試してよさそうなものを採用するようにしている● レビューに思ったより時間がかかる○ テストの追加がクラス単位なので、クラスのメソッドが多いと PR が大きくなり、レビュアーの負担になってきている => 適当な単位で区切ってはいるものの匙加減難しい
実感として● 最初はモブプロ形式でみんなで進めて、レビューされ慣れたあたりで各自担当をもってテスト追加していく、くらいがよさそう● メンバーのモチベーションが高いことに助けられている。ただでさえ負債の回収フェーズでマイナスに気持ちになりがちなので、過度なプレッシャーは取り除く● まだまだ始まったばかりなので、コツコツ進めてリファクタリングに到達するぞ!
私からのお願い頼む!アプリケーション書くときにテストコードも書いてくれ!!!頼む!