web-dev-qa-db-ja.com

モックはオープン/クローズの原則に違反していますか?

しばらく前に、私が見つけられないStack Overflowの回答で、パブリックAPIをテストする必要があることを説明する文を読み、著者はインターフェイスをテストする必要があると述べました。著者は、メソッドの実装が変更された場合、テストケースを変更する必要はないことを説明しました。これを行うと、テスト対象のシステムが機能することを保証する契約が破られるためです。つまり、メソッドが機能しない場合でもテストは失敗するはずですが、実装が変更されたためではありません。

モックについて話すとき、これは私の注意を呼びました。モックはテスト中のシステムの依存関係からの期待呼び出しに大きく依存しているため、モックはインターフェースではなく実装と密接に結び付いています。

mock vs stubを調査している間、依存関係からの期待に依存しないため、モックの代わりにスタブを使用する必要があるといういくつかの記事が同意しています。

私の質問は:

  1. モックは開閉の原則に違反していますか?
  2. 最後の段落のスタブを支持する議論に欠けているものはありますか?それはスタブをモックに対してそれほど良くないものにしますか?
  3. もしそうなら、いつモックするのに適したユースケースになり、いつスタブを使用するのに適したユースケースになるでしょうか?
13
  1. モックが開閉の原則に違反する理由はわかりません。なぜそう思われるのかを説明していただければ、懸念を軽減できる可能性があります。

  2. 私が考えることができるスタブの唯一の欠点は、それらの1つ1つが実際には依存インターフェイスの代替実装であるため、一般にモックよりも多くの作業を書く必要があることです。依存インターフェースの実装。極端な例を示すと、テスト中のサブシステムがRDBMSを呼び出す場合、RDBMSのモックは、テスト中のサブシステムによって発行されることがわかっている特定のクエリに単純に応答し、所定のテストデータセットを生成します。一方、代替の実装は本格的なインメモリRDBMSであり、本番環境で使用している実際のクライアント/サーバーRDBMSの癖をエミュレートする必要があるという余分な負担がかかる可能性があります。 (幸いにも、私たちはHSQLDBのようなものを持っているので、実際にそれを行うことができますが、それでも、それは特にSQL標準に固執し、他人の癖をエミュレートする必要がないため、少し面倒です。)

  3. モックの適切な使用例は、依存するインターフェースが複雑すぎて代替の実装を作成できない場合や、モックを1回だけ記述し、再度触れることはないと確信している場合です。このような場合は、先に進んで、すばやく汚れたモックを使用してください。したがって、スタブ(代替実装)の適切な使用例は、ほとんどすべての場合です。特に、テスト中のサブシステムとの長期的な関係を予測している場合は、代わりに、すてきでクリーンな代替実装を使用してください。インターフェイスが変更された場合にのみ、インターフェイスをメンテナンスする必要がなく、メンテナンスが必要になります。テスト中のサブシステムの実装が変更されるたびにおよびが変更されます。

追伸あなたが参照している人は、programmers.stackexchange.comのここにある他のテスト関連の回答の1つで、たとえば this one のように、私であった可能性があります。

4
Mike Nakis
  1. Open/Closedの原則は、ほとんどの場合、クラスを変更せずに動作を変更できることです。したがって、テスト対象のクラスの内部にモック化されたコンポーネントの依存関係を挿入しても、違反にはなりません。

  2. テストダブル(モック/スタブ)の問題は、基本的に、テスト中のクラスがその環境とどのように相互作用するかに関して任意の仮定を行うことです。これらの期待が間違っている場合でも、コードがデプロイされると問題が発生する可能性があります。余裕がある場合は、実稼働環境の境界と同じ制約内でコードをテストしてください。できない場合は、可能な限り最小限の仮定を行い、システムの周辺機器(データベース、認証サービス、HTTPクライアントなど)のみを模擬/スタブします。

IMHO、doubleを使用する必要がある唯一の正当な理由は、テスト中のクラスとの相互作用を記録する必要がある場合、または偽のデータを提供する必要がある場合です(両方の手法で実行できます)。ただし、これを悪用することは、設計が不適切であること、またはテスト実装のAPIに依存しすぎているテストを反映していることに注意してください。

9
Francis Toth

注:私はあなたがMockを「実装のないクラス、監視できるもの」を意味し、Stubを「部分的なモック、別名実装されたクラスの実際の動作の一部を使用する」と定義していると仮定しています- このスタックオーバーフローの質問

コンセンサスがスタブを使用することであると考える理由がわかりません。たとえば、 Mockito Documentation の反対です

いつものように、部分的な模擬警告を読みます。オブジェクト指向プログラミングは、複雑さを個別の特定のSRPyオブジェクトに分割することにより、複雑さへの取り組みが少なくなります。部分モックはどのようにこのパラダイムに適合しますか?まあ、そうではありません...部分的なモックは通常、複雑さが同じオブジェクトの別のメソッドに移動されたことを意味します。ほとんどの場合、これはアプリケーションの設計方法ではありません。

ただし、部分モックが便利な場合はまれにあります。簡単に変更できないコードを処理する(サードパーティのインターフェイス、レガシーコードの暫定的なリファクタリングなど)。ただし、部分的なモックは、新しいテスト駆動型で十分に使用しません。設計されたコード。

そのドキュメントはそれが私よりも良いと言います。モックを使用すると、特定の1つのクラスだけをテストできます。探している動作を実現するために部分的なモックが必要な場合は、おそらく何かが間違っている、SRPに違反している、などがあり、コードはリファクタリングに耐えることができます。モックはしない開閉の原則に違反します。なぜなら、これらはいずれにしてもテストでのみ使用されるため、そのコードに対する実際の変更ではないからです。通常、それらはとにかくcglibのようなライブラリによってその場で生成されます。

6
durron597

この問題は、有効なテストはオープン/クローズドテストを満たすものだけであるという仮定から生じていると思います。

重要なのは、インターフェイスをテストするテストだけであることは簡単にわかります。ただし、実際には、多くの場合、内部の仕組みをテストすることによってそのインターフェイスをテストする方が効果的です。

たとえば、「実装は例外をスローしてはならない」などの否定的な要件をテストすることはほぼ不可能です。ハッシュマップを使用して実装されているマップインターフェースについて考えてみます。物事を再ハッシュしなければならない場合(危険な場合があります)でも、ハッシュマップがスローすることなく、マップインターフェイスと確実に一致するようにする必要があります。入力のすべての組み合わせをテストして、インターフェイスの要件を満たしていることを確認できますが、宇宙の熱死よりも時間がかかる可能性があります。代わりに、カプセル化を少し解除して、より緊密に相互作用するモックを開発し、ハッシュマップが、再ハッシュアルゴリズムがスローしないようにするために必要な正確な再ハッシュを強制的に実行します。

Tl/Dr:「本を読んで」それを行うのはいいことですが、Pushが駆けつけたとき、金曜日までに上司の机に製品を置くことは、熱中症になるまでかかる本ごとのテストスイートよりも便利です。適合性を確認するための宇宙。

2
Cort Ammon