web-dev-qa-db-ja.com

具象クラスを模擬することをお勧めしますか?

フレームワークのWebサイトをモックする例のほとんどは、インターフェイスをモックすることです。私が現在使用しているNSubstituteとしましょう。それらのモックの例はすべて、インターフェイスをモックすることです。

しかし実際には、一部の開発者が具象クラスを模擬するのを見ました。具象クラスを模擬することをお勧めしますか?

35
DonDon

理論的には、具象クラスをあざける問題はまったくありません。 (キーワードinterfaceではなく)論理インターフェースに対してテストを行っていますが、その論理インターフェースがclassによって提供されているかinterfaceによって提供されているかは関係ありません。

実際には、.NET/C#はこれを少し問題にします。 .NETモックフレームワークについて述べたように、私はあなたがそれに制限されていると仮定します。

.NET/C#では、メンバーはデフォルトで非仮想であるため、動作をモックする(つまり、クラスから派生し、すべてのメンバーをオーバーライドしてテスト固有のものを行う)プロキシベースのメソッドは、メンバーを明示的にマークしない限り機能しません。 virtualとして。これは問題を引き起こします:単体テストで完全に安全であることを意図したモッククラスのインスタンスを使用しています(つまり、実際のコードを実行しない)が、すべてがvirtualであることを確認していないと、最終的には実際のコードとモックされたコードが混在して実行されます(これは、常に実行されるコンストラクターロジックがあり、更新される他の具体的な依存関係がある場合はさらに悪化します)。

これを回避するにはいくつかの方法があります。

  • interfacesを使用します。これは機能し、 NSubstituteのドキュメント で助言しますが、実際には必要ない可能性があるインターフェイスでコードベースが肥大化する可能性があるという欠点があります。間違いなく、コードに優れた抽象化が見つかれば、当然、テストできる再利用可能なインターフェイスがきちんと整ってしまいます。私はそれがそのようにパンアウトするのを見たことはありませんが、YMMVです:)
  • すべてを仮想化することに熱心に取り組みます。これについて議論の余地のある欠点は、テストのためにクラス全体の動作を変更したいだけの場合に、これらのすべてのメンバーがデザインの拡張ポイントになることを提案していることです。また、コンストラクターロジックの実行を停止せず、具象クラスが他の依存関係を必要とする場合にも役立ちません。
  • Virtuosity add-in for Fody のようなものを介してアセンブリの書き換えを使用します。これを使用して、アセンブリのすべてのクラスメンバーを仮想に変更できます。
  • TypeMock(paid)JustMock(paidのような非プロキシベースのモッキングライブラリを使用します)Microsoft Fakes(VS Ultimate/Enterpriseが必要ですが、前任者である Microsoft Moles は無料です)または Prig(無料+オープンソース)。これらは、静的メンバーだけでなく、クラスのすべての側面を模擬できると思います。

最後のアイデアに対して提出された一般的な不満は、「偽の」継ぎ目を介してテストしているということです。コードを拡張してコードの動作を変更するために通常使用されるメカニズムの外に出ます。これらのメカニズムの外に出る必要があるということは、設計の厳格さを示している可能性があります。私はこの議論を理解していますが、別のインターフェースを作成することのノイズがメリットを上回るケースを見てきました。潜在的な設計上の問題を認識しておくことが問題だと思います。設計の剛性を強調するためにテストからのフィードバックが必要ない場合、それらは優れたソリューションです。

最後に、テストでユニットのサイズを変更する方法を説明します。通常、1つのユニットとして1つのクラスがあります。ユニットとしていくつかのまとまりのあるクラスがあり、そのコンポーネントの周りに明確に定義された境界として機能するインターフェースがある場合は、多くのクラスをモックする必要がなく、より安定した境界をモックするだけで済みます。これにより、テストがより複雑になる可能性があります。利点としては、機能のまとまりのあるユニットをテストし、そのユニットの周りに堅固なインターフェースを開発することが推奨されるという利点があります。

お役に立てれば。

71
David Tchepak

更新

3年後、気が変わったことを認めたいと思います。

理論的には、モックオブジェクトの作成を容易にするためだけにインターフェイスを作成するのは好きではありません。実際には(私はNSubstituteを使用しています)、複数のパラメーターを持つ実際のクラスをモックするよりもSubstitute.For<MyInterface>()を使用する方がはるかに簡単です。 Substitute.For<MyCLass>(mockedParam1, mockedParam2, mockedParam3)、各パラメーターは個別にモックする必要があります。その他の潜在的な問題は NSubstituteのドキュメント で説明されています

私たちの会社では、現在推奨されているプラ​​クティスはインターフェースを使用することです

元の答え

同じ抽象の複数の実装を作成する必要がない場合は、インターフェースを作成しないでください。それが David Tchepakによって指摘された なので、実際に必要とされない可能性のあるインターフェースでコードベースを肥大化したくないでしょう。

http://blog.ploeh.dk/2010/12/02/InterfacesAreNotAbstractions.aspx から

疎結合を可能にするためにクラスからインターフェースを抽出しますか?その場合、おそらくそれらを実装する インターフェイスと具象クラスの間の1:1の関係 があります。それはおそらく 良い兆候ではない であり、 再利用された抽象化の原則(RAP) に違反しています。

特定のインターフェイスの実装が1つしかないのはコードの匂いです

ターゲットがテスト容易性である場合、 上記のDavid Tchepakの回答 の2番目のオプションを使用します。

ただし、すべてを仮想化する必要があるとは思いません。代替するメソッドのみを仮想化するだけで十分です。また、メソッドが仮想であることをメソッド宣言の横にコメントを追加して、単体テストのモッキングの代わりに使用できるようにします。

ただし、インターフェースではなく具象クラスの置換にはいくつかの制限があることに注意してください。例えば。 NSubstituteの場合

注:クラスを作成して使用すると、望ましくない副作用が生じる可能性があるため、クラスの再帰的な代用は作成されません。

問題はむしろです。なぜでしょうか。

これが役立つシナリオがいくつかあります。

具象クラスの実装はまだ完了していないか、それを実行した人が信頼できません。そのため、クラスを指定どおりに模擬し、コードをテストします。

また、データベースアクセスなどを行うクラスをモックするのにも役立ちます。テストデータベースがない場合は、常に定数であるテストの値を返すことができます(クラスをモックすることで簡単です)。

1
konqi

それが推奨されるということではなく、他に選択の余地がなければこれを行うことができるということです。

通常、適切に設計されたプロジェクトは、個別のコンポーネントのインターフェースの定義に依存しているため、他のコンポーネントをモックすることにより、コンポーネントを個別にテストできます。しかし、あなたがレガシーコード/変更できないコードを使用していて、それでもクラスをテストしたい場合は、選択の余地がなく、批評することはできません(あなたがこれらのコンポーネントをインターフェースに切り替えてみて、その権利を拒否されました。

0
jolivier