8

私はまだユニットテスト、特にモックに関して学習段階にあります(私はPascalMockDUnitフレームワークを使用しています)。私が今つまずいたことの1つは、テストされたクラス/インターフェイスの実装の詳細を単体テストにハードコーディングする方法が見つからず、それが間違っていると感じたことです...

例:アプリケーション設定(基本的には名前と値のペア)を読み書きするための非常に単純なインターフェイスを実装するクラスをテストしたいと思います。コンシューマーに提示されるインターフェイスは、値が実際に保存される場所と方法(レジストリ、INIファイル、XML、データベースなど)に完全に依存しません。当然、アクセスレイヤーは、構築時にテストされたクラスに注入されるさらに別のクラスによって実装されます。このアクセスレイヤーのモックオブジェクトを作成しました。これで、レジストリ/ INIファイルなどを実際に読み書きすることなく、インターフェイス実装クラスを完全にテストできるようになりました。

ただし、テストされたクラスがアクセスしたときにモックが本物とまったく同じように動作することを確認するには、単体テストで、テストされたクラスが期待するメソッド呼び出しと戻り値を非常に明示的に定義して、モックオブジェクトを設定する必要があります。つまり、アクセスレイヤーのインターフェイスや、テスト対象のクラスがそのレイヤーを使用する方法を変更する必要がある場合は、インターフェイスであっても、そのインターフェイスを内部的に使用するクラスの単体テストも変更する必要があります。私が実際にテストしているクラスの一部はまったく変更されていません。これは、モックを使用するときに私が一緒に暮らさなければならないものですか、それともこれを回避するクラス依存関係を設計するためのより良い方法がありますか?

4

3 に答える 3

7

テストされたクラスがアクセスしたときにモックが本物とまったく同じように動作することを確認するには、単体テストで、テストされたクラスが期待するメソッド呼び出しと戻り値を非常に明示的に定義して、モックオブジェクトを設定する必要があります。

正しい。

アクセスレイヤーのインターフェイスまたはテストされたクラスがそのレイヤーを使用する方法の変更私も単体テストを変更する必要があります

正しい。

私が実際にテストしているクラスのインターフェースはまったく変更されていませんが。

「実際にテスト」?公開されたインターフェイスクラスを意味しますか?それはいいです。

「テスト済み」(インターフェース)クラスがアクセスレイヤーを使用する方法は、内部インターフェースをアクセスレイヤーに変更したことを意味します。インターフェイスの変更(内部の変更も含む)にはテストの変更が必要であり、何か間違ったことをすると破損する可能性があります。

これには何の問題もありません。実際、要点は、アクセスレイヤーへの変更は、変更が「機能する」ことを保証するためにモックへの変更を必要とする必要があるということです。

テストは「堅牢」であるとは考えられていません。もろいはずです。内部の動作を変更する変更を加えると、問題が発生する可能性があります。テストの堅牢性が高すぎると、何もテストされません。ただ機能するだけです。そして、それは間違っています。

テストは、正確に正しい理由でのみ機能する必要があります。

于 2010-08-10T11:16:35.853 に答える
6

これは、モックを使用するときに私が一緒に暮らさなければならないものですか、それともこれを回避するクラス依存関係を設計するためのより良い方法がありますか?

多くの場合、モック(特にJMockのような機密性の高いフレームワーク)は、テストしようとしている動作に直接関係しない詳細を説明するように強制します。これは、実行しすぎている疑わしいコードを公開することで役立つ場合もあります。呼び出し/依存関係が多すぎます。

しかし、あなたの場合、私があなたの説明を正しく読んだら、あなたは本当に問題がないように思えます。読み取り/書き込みレイヤーを正しく、適切なレベルの抽象化で設計すれば、変更する必要はありません。

つまり、アクセスレイヤーのインターフェイスや、テスト対象のクラスがそのレイヤーを使用する方法を変更する必要がある場合は、インターフェイスであっても、そのインターフェイスを内部的に使用するクラスの単体テストも変更する必要があります。私が実際にテストしているクラスの一部はまったく変更されていません。

これを回避するために抽象化されたアクセスレイヤーを作成するのがポイントではありませんか?一般に、オープン/クローズの原則に従って、この種のインターフェースは変更されるべきではなく、それを消費するクラスとの契約を破るべきではありません。ひいては、単体テストも破ることはありません。これで、メソッド呼び出しの順序を変更したり、抽象化レイヤーに対して新しい呼び出しを行う必要がある場合、特に一部のフレームワークでは、モックの期待が破られます。これはモックを使用するコストのほんの一部であり、完全に許容できます。ただし、一般に、インターフェイス自体は安定している必要があります。

于 2010-08-10T16:55:56.967 に答える
1

あなたの例にいくつかの名前を付けるために、

  • RegistryBasedDictionaryは、ロール(インターフェイス)ディクショナリを実装します。
  • RegistryBasedDictionaryは、RegistryWinAPIWrapperによって実装されるRoleRegistryAccessorに依存しています。

現在、RegistryBasedDictionaryのテストに関心があります。単体テストは、RegistryAccessorロールのモック依存関係を挿入し、依存関係との予想される相互作用をテストします。

  • 不必要なテスト保守を回避するためのここでの秘訣は、「何が起こるかを正確に指定することです。これ以上はありません。」(GOOSブック(モックフレーバーのTDDについては必読)から。したがって、依存関係メソッドの呼び出しの順序は重要ではありません。 、テストで指定しないでください。これにより、実装での呼び出しの順序を自由に変更できます。)
  • 実際の実装からのリークが含まれないようにロールを設計します。ロールの実装に依存しないようにします。

RegistryBasedDictionaryテストを変更する唯一の理由は、RegistryBasedDictionaryの動作の変更であり、その依存関係の変更ではありません。したがって、依存関係または役割/コントラクトとの相互作用が変更された場合は、テストを更新する必要があります。これは、個別にテストする柔軟性のために支払う必要のあるインタラクションベースのテストの価格です。ただし、実際には、それほど頻繁には発生しません。

于 2010-08-11T10:02:14.697 に答える