モックフレームワークのWebサイトに示されている例のほとんどは、インターフェイスをモックすることです。私が現在使用しているNSubstituteを例にとると、それらのモックの例はすべて、インターフェースをモックすることです。
しかし実際には、代わりに開発者のモックコンクリートクラスを見ました。具体的なクラスをモックすることをお勧めしますか?
モックフレームワークのWebサイトに示されている例のほとんどは、インターフェイスをモックすることです。私が現在使用しているNSubstituteを例にとると、それらのモックの例はすべて、インターフェースをモックすることです。
しかし実際には、代わりに開発者のモックコンクリートクラスを見ました。具体的なクラスをモックすることをお勧めしますか?
理論的には、具体的なクラスをモックすることはまったく問題ありません。(キーワードではなく)論理インターフェースに対してテストを行っており、その論理インターフェースがaまたは。のどちらinterface
で提供されているかは関係ありません。class
interface
実際には、.NET / C#はこれを少し問題にします。.NETモックフレームワークについてお話ししたように、私はあなたがそれに制限されていると仮定します。
.NET / C#では、メンバーはデフォルトで非仮想であるため、プロキシベースのモック動作のメソッド(つまり、クラスから派生し、すべてのメンバーをオーバーライドしてテスト固有の処理を実行する)は、メンバーを明示的にマークしない限り機能しません。としてvirtual
。これは問題につながります:ユニットテストで完全に安全であることが意図されている(つまり、実際のコードを実行しない)モッククラスのインスタンスを使用していますが、すべてが正しいことを確認しない限り、virtual
最終的には実行中の実際のコードとモックコードの組み合わせ(これは、常に実行されるコンストラクターロジックがある場合に特に問題になる可能性があり、新しく作成する他の具体的な依存関係がある場合は複雑になります)。
これを回避する方法はいくつかあります。
interfaces
ます。これは機能し、NSubstituteのドキュメントでアドバイスしていますが、実際には必要ない可能性のあるインターフェイスでコードベースが肥大化する可能性があるという欠点があります。間違いなく、コードに優れた抽象化が見つかった場合、テストできるきちんとした再利用可能なインターフェースに自然になります。私はそれがそのようにパンアウトするのを見たことがありませんが、YMMV。:)最後のアイデアに対して提起された一般的な苦情は、「偽の」シームを介してテストしているというものです。コードの動作を変更するためにコードを拡張するために通常使用されるメカニズムの外に出ます。これらのメカニズムの外側に出る必要があることは、設計の剛性を示している可能性があります。私はこの議論を理解していますが、別のインターフェースを作成することのノイズが利点を上回る場合を見てきました。潜在的な設計上の問題を認識することが問題だと思います。設計の剛性を強調するためにテストからのフィードバックが必要ない場合は、優れたソリューションです。
私がそこに投げ出す最後のアイデアは、テストでユニットのサイズを変更して遊ぶことです。通常、1つのクラスとして1つのクラスがあります。ユニットとして多数のまとまりのあるクラスがあり、そのコンポーネントの周囲に明確に定義された境界として機能するインターフェイスがある場合、多くのクラスをモックする必要はなく、より安定した境界をモックするだけで済みます。これにより、テストがより複雑になる可能性があります。機能のまとまりのあるユニットをテストし、そのユニットの周囲に堅固なインターフェイスを開発することが奨励されるという利点があります。
お役に立てれば。
更新:
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の関係を持っているでしょう。これはおそらく良い兆候ではなく、 Reused Abstractions Principle(RAP)に違反してい ます。
特定のインターフェースの実装が1つしかないのは、コードの臭いです。
ターゲットがテスト容易性である場合、上記のDavidTchepakの回答から2番目のオプションを選択します。
しかし、私はあなたがすべてを仮想化する必要があるとは確信していません。代わりに使用するメソッドのみを仮想化するだけで十分です。また、メソッド宣言の横に、メソッドは仮想であるというコメントを追加して、単体テストのモックに置き換えることができるようにします。
ただし、インターフェイスの代わりに具象クラスを置き換えるには、いくつかの制限があることに注意してください。例:NSubstitute
注:クラスを作成して使用すると、望ましくない副作用が発生する可能性があるため、クラスの再帰的な置換は作成されません。
。
問題はむしろです:なぜそうではないのですか?
これが役立ついくつかのシナリオを考えることができます。たとえば、次のようになります。
具体的なクラスの実装はまだ完了していないか、それを実行した人は信頼できません。そのため、指定されたとおりにクラスをモックし、コードをテストします。
データベースアクセスなどを行うクラスをモックすることも役立ちます。テストデータベースがない場合は、常に一定のテスト値を返したい場合があります(これはクラスをモックすることで簡単です)。
推奨されているわけではありません。他に選択肢がない場合は、これを実行できます。
通常、適切に設計されたプロジェクトは、個別のコンポーネントのインターフェイスの定義に依存しているため、他のコンポーネントをモックすることで、各コンポーネントを個別にテストできます。しかし、変更が許可されていないレガシーコード/ codeを使用していて、それでもクラスをテストしたい場合は、選択の余地がなく、それを批判することはできません(これらのコンポーネントをインターフェイスに切り替えようと努力したと仮定します)との権利を拒否されました)。
私たちが持っていると仮定します:
class Foo {
fun bar() = if (someCondition) {
“Yes”
} else {
“No”
}
}
テストコードで次のモックを実行することを妨げるものは何もありません。
val foo = mock<Foo>()
whenever(foo.bar()).thenReturn(“Maybe”)
問題は、クラスFooの誤った動作を設定していることです。クラスFooの実際のインスタンスは、「Maybe」を返すことはできません。