Xというクラスがあり、X1の振る舞いを検証するユニットテストをいくつか書きました。 また、X を依存関係とするクラス A もあります。
つまり、A をユニットテストするときに、X のモックの振る舞いを X1 に設定(仮定)するのです。 時間が経ち、人々があなたのシステムを使い、ニーズが変化し、Xが進化し、あなたはXを修正しX2の振る舞いを示すようにします。明らかに、Xに対するユニットテストは失敗し、あなたはそれらを適応させる必要があります。
しかし、Aについてはどうでしょうか?X の振る舞いが変更されても、A のユニットテストは失敗しません (X のモッキングのため)。本物の (変更された) X で実行したときに A の結果が異なることを、どのように検出するのでしょうか?
私は、「それはユニットテストの目的ではない」という答えを期待しているのですが、ではユニットテストにはどんな価値があるのでしょうか?すべてのテストがパスしたときに、本当に破壊的な変更を導入していないことを伝えるだけなのでしょうか? そして、あるクラスの振る舞いが(自発的にせよ、非自発的にせよ)変更されたとき、その結果をすべて(できれば自動化された方法で)検出することができるでしょうか?統合テストにもっと注力すべきではないでしょうか?
Aのユニットテストを書くときに、Xをモックする。
するのでしょうか?私は、どうしても必要なとき以外は、しません。どうしても必要な場合はそうします。
1.Xが遅い、または 2.X
に副作用がある場合
どちらも当てはまらない場合、 A
のユニットテストは X
もテストすることになる。それ以外のことをすると、テストの分離を非論理的に極端にしてしまうことになります。
もし、あなたのコードの一部が他の部分のモックを使っているのであれば、私は同意します。だから、こんなことはしないでください。そのようなテストは、実際の依存関係を使用させましょう。その方が、はるかに価値のあるテストを形成できます。
そして、もし一部の人がこれらのテストを「ユニットテスト」と呼ぶことに腹を立てるなら、単に「自動テスト」と呼び、良い自動テストを書くことに専念してください。
両方が必要です。 各ユニットの動作を確認するためのユニットテストと、それらが正しく接続されていることを確認するためのいくつかの統合テストです。 統合テストだけに頼った場合の問題は、すべてのユニット間の相互作用による組合せの爆発です。
例えば、クラス A があって、すべてのパスを完全にカバーするために 10 個のユニットテストが必要だとします。 そして、別のクラス B があり、そこでもコードが通ることができるすべての経路をカバーするために 10 のユニットテストが必要だとします。 今、あなたのアプリケーションで、Aの出力をBに送る必要があるとします。今、あなたのコードは、Aの入力からBの出力まで100の異なる経路を取ることができます。
ユニットテストでは、すべてのケースを完全にカバーするために、20のユニットテストと1つの統合テストが必要なだけです。
統合テストでは、すべてのコードのパスをカバーするために100のテストが必要になります。
統合テストだけに頼ることの欠点について、とても良いビデオがあります [J B Rainsberger Integrated Tests Are A Scam HD][1] 。
順番に質問を受ける
では、ユニットテストにはどのような価値があるのでしょうか。
ユニットテストは、安価に作成・実行でき、早期にフィードバックが得られます。もし、Xを壊したとしても、良いテストがあれば、すぐに見つけることができます。すべてのレイヤーをユニットテストしない限り(そう、データベースでさえも)、統合テストを書くことを検討しないでください。
すべてのテストが合格したら、本当にテストが完了したことになるのでしょうか? テストに合格したときだけ、本当にテストに合格したと言えるのでしょうか?
テストが合格していることは、実際にはほとんどわかりません。十分なテストを書いていない可能性があります。十分なシナリオをテストしていない可能性があります。コードカバレッジは、このような場合に役立ちますが、銀の弾丸ではありません。常に合格しているテストがあるかもしれません。そのため、赤はしばしば見落とされがちな最初のステップである、赤、緑、リファクタリングとなるのです。
そして、あるクラスの挙動が(自発的に、あるいは非自発的に)変化したとき。
そして、あるクラスの振る舞いが(喜んで、あるいは嫌々ながら)変更されたとき、どのようにして(できれば自動化された方法で)すべての 結果
テストを増やす - ツールはどんどん良くなってきていますが。しかし、クラスの振る舞いをインターフェイスで定義する必要があります(下記参照)。注:テストピラミッドの頂点には、常に手動テストの場所があります。
統合テストにもっと力を入れるべきじゃないですか?
統合テストは、書くにも、実行するにも、維持するにも、コストがかかります。ビルドの設定によっては、ビルドマネージャがテストを除外してしまい、開発者の記憶に頼ってしまうかもしれません(決して良いことではありません!)。
私は、開発者が壊れた統合テストを修正するために何時間も費やしているのを見たことがあります。これができなければ、ソフトウエアを動かしてみてください。エンドユーザが気にするのはその点だけです。
もし、クラスAがクラスXを同じように消費することを確認したいのであれば、コンクルージョンではなく、インターフェイスを使用すべきです。そうすれば、破壊的な変更がコンパイル時に検出される可能性が高くなります。