新しい職場で提案したら歓迎されたので投稿しておく。
テストコード開発方針
漫然とテストコードを書いていると、以下のような問題が発生することがある。
- テストに時間がかかりすぎ、待ち時間が発生したり、テスト結果を見なくなったりする
- テストコードの開発とレビューに時間をかけたが、そのコストに見合う利益を得られない
このような問題を避けるため、以下の方針を定める。
ビジネス上の価値に比例したテスト
コードの価値をビジネスへの影響や回避方法の有無により以下のようにランク付けする。
- メジャー
- サービスの主たる機能に影響する
- 再現条件が広い
- 回避方法がない/あっても自明でない
- マイナー
- サービスの副次的な機能に影響する
- 再現条件が限られる
- 回避方法がある
- トリビアル
- サービスには影響しない
- 違和感はあるが、不便を感じない
- 回避する必要がない
複数のランクに該当する場合、より多く該当するランクに分類する。
テスト対象がメジャーに該当する場合、より大量の/時間のかかるのテストが許容される。マイナーに該当する場合、最低限の正常系のテストが許容される。トリビアルの場合、テストは書かない。
インテグレーションテストは最低限
インテグレーションテストの対象は以下のものに限る:
- View - Controller - Model間のインターフェース
- 複数のHTTPリクエストに依存する処理
なお、ViewにはJavaScriptを含む。以下に具体例を挙げる:
- リンク/ボタン/フォームに起因するリクエストパラメータとControllerによるパースの整合
- リクエストパラメータとModelのscopeの整合
- Controllerが生成するインスタンスとViewの表示結果の整合
- 複数のユーザのインタラクション
ただし、インテグレーションテストは低速なので、各対象につきテストは1回に抑える。値を変えた網羅的なテストはユニットテストで行うこと。
本来のControllerの責務だけをテスト
Controllerのテストを書く場合、その対象は以下のものに限る:
- 他のクラスのメソッド呼び出し
- HTTPステータスコード
- リダイレクト先URL
- cookies / sessions
- Flash
Controllerが上記以外の処理をしている場合は、その処理を以下の手順で別のクラスに展開する:
- 問題の処理のテストをControllerテストとして書く
- 問題の処理を別のクラスに展開する
- テストを移動する。Controllerのテストでは、新しいクラスの利用だけをテストする。
Controllerが生成しViewで利用するインスタンスのテストは、必要ならインテグレーションテストにする(assignsとassert_templateはDeprecatedです。
privateメソッドのテストを書かない
privateメソッドのテストは以下の理由で書くべきでない:
- privateメソッドのテストは実装の自由度を下げ、リファクタリングを阻害する
- オブジェクト指向プログラミングでテストすべきはオブジェクト間のインターフェースであり、privateメソッドはインターフェースではない
テスト対象がやっていない処理はスタブ/モックにする
テスト対象外のオブジェクトが必要な場合はスタブやモックを利用する。
ただし、ControllerがModelのscopeをチェインして利用する場合など、複数の解法がある場合は実オブジェクトを使ってよい。
コミュニケーション優先
以上の方針よりも開発チーム内でのコミュニケーションが優先される。