12

他のクラスを (メンバーとして、またはメソッドの引数として) 使用するクラスには、単体テスト用に適切に動作するインスタンスが必要です。これらのクラスが利用可能で、追加の依存関係が導入されていない場合は、モックの代わりに本物を使用する方がよいのではないでしょうか?

4

11 に答える 11

19

できる限り実際のクラスを使用してください。

私は、「ユニット」テストの境界を可能な限り拡大することを強く信じています。現時点では、従来の意味での単体テストではなく、アプリケーションの自動回帰スイートにすぎません。私は今でも TDD を実践し、最初にすべてのテストを作成しますが、私のテストはほとんどの人よりも少し大きく、緑-赤-緑のサイクルには少し時間がかかります。しかし、これを少しの間行ってきた今、従来の意味での単体テストだけがすべてではないことを完全に確信しています。

私の経験では、一連の小さな単体テストを作成すると、将来のリファクタリングの障害になります。B を使用するクラス A があり、B をモック化して単体テストを行った場合、一部の機能を A から B に、またはその逆に移動すると、すべてのテストとモックを変更する必要があります。システムのエンド ツー エンド フローが期待どおりに機能することを検証するテストがある場合、テストは実際に、リファクタリングがシステムの外部動作の変化を引き起こした可能性のある場所を特定するのに役立ちます。

要するに、モックは特定のクラスのコントラクトを体系化し、多くの場合、実装の詳細の一部も実際に指定することになります。テスト スイート全体でモックを広範囲に使用すると、コード ベースに余分な慣性が生まれ、将来のリファクタリング作業に抵抗することになります。

于 2008-10-07T21:38:39.873 に答える
5

オブジェクトを完全に制御できる限り、「本物」を使用しても問題ありません。たとえば、プロパティとアクセサーだけを持つオブジェクトがある場合は、おそらく問題ありません。使用したいオブジェクトにロジックがある場合、問題が発生する可能性があります。

クラス a の単体テストがクラス b のインスタンスを使用し、b に導入された変更によって b が破損した場合、クラス a のテストも破損します。これは、モック オブジェクトの場合と同様に、常に正しい値を返す可能性があるという問題に遭遇する可能性がある場所です。「本物」を使用すると、テストが複雑になり、実際の問題を隠すことができます。

モックにはマイナス面もあります。いくつかのモックと、自分で見つけなければならない実際のオブジェクトとのバランスがあると思います。

于 2008-10-07T21:09:37.310 に答える
5

実際のクラスの代わりにスタブ/モックを使用する理由が1 つあります。つまり、単体テスト (純粋な単体テスト) のテスト対象クラスを他のすべてから分離することです。このプロパティは非常に便利で、テストを分離しておくことには多くの利点があります。

  • 実際のクラスの実装を呼び出す必要がないため、テストはより高速に実行されます。実装がファイル システムまたはリレーショナル データベースに対して実行される場合、テストは遅くなります。テストが遅いと、開発者は単体テストをそれほど頻繁に実行できなくなります。テスト駆動開発を行っている場合、時間を浪費するテストは開発者の時間の壊滅的な浪費です。
  • テストがテスト対象のクラスに分離されていると、問題を追跡しやすくなります。システム テストとは対照的に、スタック トレースなどで明らかに見えない厄介なバグを追跡することははるかに困難です。
  • テスト対象のクラスを純粋にテストしているため、外部クラス/インターフェースで行われた変更に対するテストの脆弱性が低くなります。脆弱性が低いことは、カップリングが低いことも示しており、これは優れたソフトウェア エンジニアリングです。
  • コード設計を決定する際に役立つ内部実装ではなく、クラスの外部動作に対してテストしています。

テストで実際のクラスを使用する場合は問題ありませんが、それは単体テストではありません。代わりに統合テストを行っています。これは、要件の検証と全体的な健全性チェックの目的に役立ちます。統合テストは、単体テストほど頻繁には実行されません。実際には、ほとんどの場合、お気に入りのコード リポジトリにコミットする前に実行されますが、同様に重要です。

覚えておく必要があるのは、次の点だけです。

  • モックとスタブは単体テスト用です。

  • 実際のクラスは、統合/システム テスト用です。

于 2008-10-08T09:07:09.407 に答える
3

依存関係がデータベースや Web サービスなどの外部システムにアクセスする場合、依存関係のモック バージョンを常に使用します。

そうでない場合は、2 つのオブジェクトの複雑さに依存します。実際の依存関係を使用してテスト対象のオブジェクトをテストすることは、本質的に 2 つの複雑さのセットを増加させることです。依存関係をモックアウトすると、テスト対象のオブジェクトを分離できます。いずれかのオブジェクトが適度に単純である場合、複雑さを組み合わせても機能し、モック バージョンは必要ありません。

他の人が言ったように、依存関係にインターフェイスを定義し、それをテスト対象のオブジェクトに注入すると、モックアウトがはるかに簡単になります。

個人的には、厳密なモックを使用して依存関係へのすべての呼び出しを検証する価値があるかどうかについては未定です。普段はそうですが、ほとんど習慣です。

次の関連する質問も役立つ場合があります。

于 2008-10-08T01:08:18.030 に答える
3

私の回答から抽出および拡張された継承オブジェクトの単体テスト方法は?">こちら:

可能であれば、常に実際のオブジェクトを使用する必要があります。

実際のオブジェクトが設定したくないことを行う場合にのみ、モック オブジェクトを使用する必要があります (ソケット、シリアル ポートの使用、ユーザー入力の取得、大量のデータの取得など)。基本的に、モック オブジェクトは、実際のオブジェクトを使用してテストを実装および維持するための推定労力が、モック オブジェクトを使用してテストを実装および維持するための労力よりも大きい場合に使用されます。

私は「依存するテストの失敗」という議論には賛成しません。依存先のクラスが壊れたためにテストが失敗した場合、テストは実行すべきことを正確に実行しました。これは匂いではありません!依存するインターフェースが変わったら知りたい!

高度にモック化されたテスト環境は、特にプロジェクトの初期段階でインターフェースが流動的である場合、メンテナンスに非常に手間がかかります。できるだけ早く統合テストを開始する方が良いといつも思っています。

于 2008-10-08T09:37:23.200 に答える
2

何度も噛まれて以来、私は嘲笑された物に非常に嫌気がさしてきました。分離された単体テストが必要な場合は優れていますが、いくつかの問題があります。主な問題は、OrderクラスがOrderItemオブジェクトのコレクションを必要とし、それらをモックした場合、モックされたOrderItemクラスの動作が実際の例と一致することを確認することはほとんど不可能であるということです(適切なシグネチャでメソッドを複製することは一般的にありません)足りる)。モックされたクラスが実際のクラスと一致せず、エッジケースをキャッチするための十分な統合テストが実施されていなかったために、システムが失敗するのを何度も見ました。

I generally program in dynamic languages and I prefer merely overriding the specific methods which are problematic. Unfortunately, this is sometimes hard to do in static languages. The downside of this approach is that you're using integration tests rather than unit tests and bugs are sometimes harder to track down. The upside is that you're using the actual code that is written, rather than a mocked version of that code.

于 2008-10-08T08:50:57.613 に答える
2

最初にそれ自体が単体テストされている場合にのみ、本物を使用してください。それを妨げる依存関係が導入された場合 (循環依存関係、または最初に他の特定の手段を講じる必要がある場合) は、「モック」クラス (通常は「スタブ」オブジェクトと呼ばれます) を使用します。

于 2008-10-07T21:19:31.003 に答える
1

あなたの「本物」が JavaBeans のような単純な値オブジェクトであれば、それで問題ありません。

より複雑なものについては、モッキングフレームワークから生成されたモックが、呼び出されるメソッドの数、正確なシーケンス、毎回予想されるパラメーターなど、それらがどのように使用されるかについて正確な期待を与えることができるので、私は心配します. 実際のオブジェクトはこれを行うことができないため、テストで深みを失うリスクがあります。

于 2008-10-07T21:35:58.193 に答える
1

UnitUnderTest が Thing とどのように相互作用するかについての期待を検証することを気にせず、RealThing との相互作用に他の副作用がない (またはこれらを嘲笑することができる) 場合は、私の意見では、あなたのUnitUnderTest は RealThing を使用します。

テストがコードベースのより多くをカバーすることは、おまけです。

通常、RealThing の代わりに ThingMock を使用する必要がある場合は、簡単に判断できます。

  • モノとのやり取りで期待を検証したいとき。
  • RealThing を使用すると、望ましくない副作用が生じる場合があります。
  • または、RealThing がテスト設定で使用するには難しすぎる/面倒な場合。
于 2008-10-07T22:12:06.937 に答える
0

It depends on your coding style, what you are doing, your experience and other things.

Given all that, there's nothing stopping you from using both.

I know I use the term unit test way too often. Much of what I do might be better called integration test, but better still is to just think of it as testing.

So I suggest using all the testing techniques where they fit. The overall aim being to test well, take little time doing it and personally have a solid feeling that it's right.

Having said that, depending on how you program, you might want to consider using techniques (like interfaces) that make mocking less intrusive a bit more often. But don't use Interfaces and injection where it's wrong. Also if the mock needs to be fairly complex there is probably less reason to use it. (You can see a lot of good guidance, in the answers here, to what fits when.)

Put another way: No answer works always. Keep your wits about you, observe what works what doesn't and why.

于 2008-10-09T23:22:44.390 に答える
0

インターフェースの観点からコードを作成すると、テスト対象のクラスに任意のクラスの偽のバージョンを簡単に挿入できるため、単体テストが楽しくなります。

たとえば、何らかの理由でデータベース サーバーがダウンしている場合でも、ハッシュ マップなどでメモリに格納されたクック済みデータを含む偽のデータ アクセス クラスを作成することで、ユニット テストを実行できます。

于 2008-10-07T21:40:51.647 に答える