モックオブジェクトを使用する際に、ユニットテストが実装の内部に関する知識を必要とすることを防ぐにはどうすればよいですか?
-
27-09-2019 - |
質問
私はまだユニットテストに関して学習段階にあり、特にモッキングに関するものです(私は使用しています パスカルモック と ダニット フレームワーク)。私が今つまずいたことの1つは、テストされたクラス/インターフェイスのハードコーディングの実装の詳細を自分のユニットテストに導く方法を見つけることができず、それが間違っていると感じていることです...
たとえば、アプリケーションの設定を読み書きするための非常にシンプルなインターフェイスを実装するクラスをテストしたいと思います(基本的に名前/値ペア)。消費者に提示されるインターフェイスは、値が実際に保存される場所とどのように保存されるかに完全に不可知論されます(例:レジストリ、INIファイル、XML、データベースなど)。当然のことながら、アクセス層は、建設中にテストされたクラスに注入される別のクラスによって実装されます。このアクセスレイヤー用のモックオブジェクトを作成しましたが、実際にレジストリ/INIファイル/Whatingに何かを読み書きすることなく、インターフェイスを実装するクラスを完全にテストできるようになりました。
ただし、テストされたクラスからアクセスしたときにモックが本物とまったく同じように動作するようにするために、私の単体テストは、予想されるメソッド呼び出しとテストされたクラスで期待される返品値を非常に明示的に定義することにより、モックオブジェクトを設定する必要があります。これは、アクセスレイヤーのインターフェイスまたはテストされたクラスがそのレイヤーを使用する方法に変更を加える必要がある場合は、そのインターフェイスを内部的に使用するクラスのユニットテストも変更する必要があることを意味します。 インターフェース 私が実際にテストしているクラスのうち、まったく変更されていません。これは、モックを使用するときに一緒に暮らさなければならないものですか、それともこれを避けるクラス依存を設計するより良い方法はありますか?
解決
これは、モックを使用するときに一緒に暮らさなければならないものですか、それともこれを避けるクラス依存を設計するより良い方法はありますか?
多くの場合、モック(特にJMOCKのようなデリケートなフレームワーク)により、テストしようとしている動作に直接関係しない詳細を説明することができます。通話/依存関係が多すぎます。
しかし、あなたの場合、私があなたの説明を正しく読んだ場合、あなたは本当に問題がないように聞こえます。適切なレベルの抽象化を使用して、読み取り/書き込みレイヤーを正しく設計する場合は、変更する必要はありません。
これは、アクセスレイヤーのインターフェイスまたはテスト済みクラスがそのレイヤーを使用する方法に変更を加える必要がある場合は、インターフェイスがインターフェイスであっても、そのインターフェイスを内部的に使用するクラスのユニットテストを変更する必要があることを意味します。私が実際にテストしているクラスのうち、まったく変更されていません。
これを避けるために抽象化されたアクセスレイヤーを書くポイントではありませんか?一般に、 オープン/クローズド原理, 、この種のインターフェース すべきではありません 変更して、それを消費するクラスとの契約を破るべきではなく、ひいてはそれもあなたのユニットテストを破ることはありません。ここで、メソッド呼び出しの順序を変更するか、抽象化されたレイヤーに新しい呼び出しを行う必要がある場合、はい、特にいくつかのフレームワークを使用すると、模擬期待は壊れます。これは、模擬使用のコストの一部であり、完全に受け入れられます。しかし、インターフェイス自体は、一般に、安定したままでなければなりません。
他のヒント
テストされたクラスからアクセスしたときにモックが本物とまったく同じように動作するようにするために、私の単体テストは、予想されるメソッド呼び出しと、テストされたクラスで期待される返品値を非常に明示的に定義することにより、モックオブジェクトを設定する必要があります。
正しい。
アクセスレイヤーのインターフェイスまたはテストされたクラスがそのレイヤーを使用する方法に変更する必要があります。
正しい。
私が実際にテストしているクラスのインターフェイスはまったく変更されていませんが。
「実際にテスト」?露出したインターフェイスクラスを意味しますか?それはいいです。
「テスト済み」(インターフェイス)クラスがアクセスレイヤーを使用する方法は、内部インターフェイスをアクセスレイヤーに変更したことを意味します。インターフェイスの変更(内部のものでも)がテストの変更が必要であり、何か間違ったことをした場合、破損につながる可能性があります。
これには何の問題もありません。確かに、全体のポイントは、アクセスレイヤーに変更されることです しなければならない 変更が「機能する」ことを保証するために、モックの変更が必要です。
テストは「堅牢」であると想定されていません。それは脆くなるはずです。内部動作を変える変更を加えると、物事 できる 壊す。テストが堅牢すぎると、何もテストしません。ただ機能します。そして、それは間違っています。
テストは正確な理由でのみ機能する必要があります。
あなたの例にいくつかの名前を入れるだけで、
- RegistrybasedDictionaryは、役割(インターフェイス)辞書を実装しています。
- registrybasedDictionaryは、RegistryWinapiWrapperによって実装されたロールレジストリアクセサルに依存しています。
現在、RegistryBasedDictionaryのテストに興味があります。ユニットテストは、レジストリアクセサルの役割に模擬依存関係を注入し、依存関係との予想される相互作用をテストします。
- 不必要なテストメンテナンスを避けるためのここでのトリックは、」何が起こるべきかを正確に指定します。" (から グースの本 (モックフレーバーTDDの必須読み取り)。したがって、依存メソッド呼び出しの順序が重要でない場合は、テストで指定しないでください。これにより、実装の呼び出しの順序を自由に変更できます。)
- 実際の実装からのリークが含まれていないように役割を設計します - 役割の実装に依存しないでください.
RegistryBasedictionaryテストを変更する唯一の理由は、RegistryBasedictionaryの動作の変更であり、その依存関係のいずれでもありません。その場合 その依存関係または役割/契約の変更との相互作用は、テストを更新する必要があります. 。それは、単独でテストする柔軟性のために、支払う必要があるインタラクションベースのテストの価格です。しかし、実際には、それは頻繁に起こることはありません。