Railsでのテストについて深く学ぶために「Everyday Rails - RSpecによるRailsテスト入門」を読みました。
感想
RSpecを使ったテストの書き方が主な内容で、サンプルのプロジェクト管理アプリにRSpecのテストコードを追加してテストを実行していくという学習の進め方になります。Railsを学んでからテストを学びたいって時におすすめです。ただしテストケースの作り方といったテストの基本的な考え方などは解説されていないので注意です。
とりあえずこの本でモデルスペック、システムスペックを抑えておいて、その他は必要な時に読み直すぐらいでいいんじゃないでしょうか。
難点なのが、日本語訳なので読みづらい部分があるのと説明の中で新しく追加したコードがわかりづらいことがありました。
新しく学んだこと
1章 イントロダクション
- 筆者のテストの原則
- 信頼できること
- 簡単に書けること
- 簡単に理解できること
- テストにおけるDRYではないコードは必ずしも悪ではない
2章 RSpecのセットアップ
bin/rails generate rspec:install
でrspecをインストールできる--format documentation
でデフォルトのrspecの出力を読みやすいドキュメント形式に変更すると、どのスペックがパスしたか失敗したかがわかりやすくなる- 信頼性の高いビューのテストを作ることは大変なのでビューはテストせず、UI関連のテストは結合テストに任せる
generate
コマンドで作られたファイルの中で使わないものは削除したほうがいい
3章 モデルスペック
- モデルはアプリのコアとなる部分なので、しっかりテストを行えていれば堅牢なコードになる
- モデルスペックのベストプラクティス
- 期待する結果を
describe
でまとめて記述する - 1つの
it
につき、1つの結果を期待する - どの
example
も明示的である - 各
example
の説明は動詞で始まる
- 期待する結果を
- テスト結果の
pending
は保留中という意味 - 古い
should
ではなくexample
を覚える be_valid
はモデルが有効な状態を理解できているか検証する- テストコードが意図した通りに動いているかどうか確認するためにテストが失敗することを検証する
to
をto_not
に変えて期待する結果を反転する- アプリ側のコードを失敗するように変更する
- テストを書いている最中にバリデーションについて考えることで書き忘れをなくす
- rspecで等値のエクスペクテーションを書くときは
=
ではなくeq
を使う be_empty
で値が空であるか確認するdescribe
とcontext
を使ってスペックを整理するdescribe
はクラスやシステムの機能に関するアウトラインを記述context
は特定の状態に関するアウトラインを記述
before
ブロックは各テストが実行される前に実行される- テストではDRYであるより読みやすいことの方が重要
4章 意味のあるテストデータの作成
- テストデータを作成するにはフィクスチャ、FactoryBotなどを使う
- フィクスチャ
- デフォルトで使える
- YAML形式でデータを指定する
- 動作が速い
- データをデータベースに読む込むときにActiveRecordを使わないのでバリデーションが無視されてしまう
- FacotryBot
- インストールが必要
- 多用するとテストが遅くなる
FactoryBot.create
でデータをデータベースに保存するFactoryBot.build
でデータをメモリに保存するassociation
でテーブルの関連を扱う- データの定義が重複するときは入れ子にすることで継承し簡潔に書ける
- パフォーマンス面から可能な限り
create
よりbuild
を使う - FactoryBotを使うとテストが遅くなる場合もあるので注意
5章 コントローラスペック
- コントローラのテストは壊れやすいので、コントローラのテストは削除するか、単体テストか統合テストに置き換えることが推奨されている
- レガシーなアプリではコントローラのスペックを見かけることもあるので学んでおく
- コントローラ自体が退屈なのでコントローラのテストも退屈であるべき
- コントローラの責務の1つに適切なフォーマットでデータを返すという役割があるので、html以外の形式のデータもテストする
- コントローラのテストは認可されていないユーザーとゲストユーザーに対してのアクセス制御ができているかを確認することに限定する
6章 システムスペックでUIテストをする
- モデルやコントローラが他のモデルやコントローラと一緒にうまく動作するか確認するのがシステムスペック
- システムスペックの構造はモデルやコントローラと似ている
- システムスペックの前はフィーチャスペックが使われていた
- システムスペックではブラウザの操作をシミュレートするCapybaraというgemを使う(Rails5.1以降ならデフォルトでインストールされている)
rails g rspec:system
でファイルを作成する- Capybaraを使うことで、リンクのクリックやフォームの入力などの操作をテストすることができる
- コントローラスペックはUIを無視して直接コントローラのメソッドにパラメータを送信するが、システムスペックはアプリの利用者と全く同じUIを使ってパラメータを送信する
- システムスペックでは複数のエクスペクテーションを書いたり、テストの途中でエクスペクテーションを書くのは問題ない
- Capybaraが使うドライバはJavaScriptの実行はサポートしていない
- Seleniumはテストの実行に時間がかかるので、JavaScriptのテストは必要な時だけ有効にする
- JavaScriptのテストを行うときは、
scenario
でjs: true
を指定する - フィーチャスペックよりシステムスペックを使う
7章 リクエストスペックでAPIをテストする
- JSONの出力はコントローラスペックでもできるが、堅牢なAPIを構築するにはリクエストスペックが必要
- リクエストスペックではCapybaraを使わず、代わりにHTTPに対応するメソッドを使う
- コントローラスペックと異なる点はルーティング名を自由に決めれること
8章 スペックをDRYに保つ
- テストの重複をなくすためにモジュールを使うができる
before
やcontext
ではなく、let
を使うことで使いたい時だけデータを読み込むことができるlet!
で遅延読み込みせずにブロックを即座に実行できる- テストを行うときはなるべく余計なデータをもたないようにするが、可読性にも気を付ける
- 自分でマッチャを作ることもできるが、マッチャの定義や、エラー時の出力など管理するコードも増えるので注意
- 失敗するエクスペクテーションが実行されると即座に停止して失敗を報告し、残りのエクスペクテーションは実行されない
aggregate_failures
を使って失敗を集約することで、失敗した複数のエクスペクテーションを把握することができる- システムスペックの長いコードをメソッドに切り分けてまとめることで可読性を高くできるが、使いすぎるとヘルパーメソッドが何をやって何を検証しているかを理解するためにテストスイートの中身を確認しなければならなくなるので注意する
9章 早くテストを書き、速いテストを書く
- まず動かし、次に正しくし、速くする
- いかにスペックの実行時間とテストの作成時間を速くするか
- モックはオブジェクトの代わりをするもので、データベースにアクセスしないのでテストの時間が短くなる
- スタブはオブジェクトのメソッドをオーバーライドして決められた値を返すダミーのメソッド
- モックやタグはテストの実行時間を減らすためのもの
- テストスイートに
focus: true do
と記述することで、実行したいテストだけ実行できる - タグはコミット前に全て消し、機能が完成したら必ずタグなしでテストを実行する
- 不必要なテストはコメントアウトするより
skip
を使うことで、テストを実行したときにスキップしたことが表示される - テストが遅い時は並列に実行することで速くできる
10章 その他のテスト
- テストのためのアップロードしたファイルは自動的に削除されるように設定しておく
- ファイルのアップロードをテストするときは、スペックファイルを作成→テスト用のアップロードパスを設定→テストが終わったらファイルを削除するように設定の3つのステップを行う
- 何度もファイルをアップロードする場合はファイルをモデルの属性に含めることも検討する
- rspec-railsではバックグラウンドジョブをテストするには
ActiveJob::Base.queue_adapter = :test
を指定する - メール送信はメッセージが正しく構築されているか、正しい宛先に送信されるかをテストする
- メールはバックグラウンドプロセスで送信される
- メールの詳しいテストはMailerのスペックに書き、正しく送信できているかはシステムスペックに書く
- 外部のサービスを直接テストすると、テストが遅くなったり、実際に料金が発生してしまう場合があるので、vcrのようなツールを使う
12章 最後のアドバイス
- 小さいテストで練習する
- モデルスペックからシステムスペックまでテストを進めることに慣れたら、逆からテストを書いてみる
- テストのメリットを他の開発者に売り込む