web-dev-qa-db-ja.com

Mockito.verify()を使用する場合

3つの目的でjUnitテストケースを作成します。

  1. すべての(またはほとんどの)入力の組み合わせ/値の下で、コードが必要な機能をすべて満たすようにします。
  2. 実装を変更できることを確認し、JUnitテストケースに依存して、すべての機能がまだ満たされていることを確認します。
  3. すべてのユースケースのドキュメントとして、私のコードはコードを処理し、リファクタリングの仕様として機能します-コードを書き換える必要がある場合。 (コードをリファクタリングし、私のjUnitテストが失敗した場合-おそらくいくつかのユースケースを見逃したでしょう)。

Mockito.verify()を使用する理由と時期がわかりません。 verify()が呼び出されているのを見ると、jUnitが実装を認識し始めていることがわかります。 (したがって、機能が影響を受けていなくても、実装を変更するとjUnitが破損します)。

を探しています:

  1. Mockito.verify()を適切に使用するためのガイドラインは何ですか?

  2. JUnitsがテスト対象クラスの実装を認識している、または密接に結合しているのは基本的に正しいですか?

189
Russell

クラスAのコントラクトに、タイプCのオブジェクトのメソッドBを呼び出すという事実が含まれている場合、タイプCのモックを作成し、メソッドBが呼び出されたことを確認してテストする必要があります。

これは、クラスAのコントラクトが、タイプC(インターフェースまたはクラスである可能性がある)について説明するのに十分な詳細を持っていることを意味します。そうです、私たちは単に「システム要件」を超えて、実装を説明するための何らかの方法に進む仕様のレベルについて話しているのです。

これは単体テストでは正常です。ユニットテストを行う場合、各ユニットが「正しいこと」を行っていることを確認する必要があります。これには通常、他のユニットとの相互作用が含まれます。ここでいう「ユニット」とは、クラス、またはアプリケーションのより大きなサブセットを意味する場合があります。

更新:

これは検証だけでなく、スタブにも当てはまると思います。コラボレータークラスのメソッドをスタブ化するとすぐに、ユニットテストはある意味、実装に依存するようになります。そうなるのは、単体テストの性質のようなものです。 Mockitoは検証と同じくらいスタブに関するものであるため、Mockitoを使用しているという事実は、この種の依存関係を実行することを意味します。

私の経験では、クラスの実装を変更する場合、それに合わせてユニットテストの実装を変更する必要があります。ただし、通常、ユニットテストのインベントリを変更する必要はありませんareクラスに対して。もちろん、変更の理由は、以前にテストできなかった条件の存在でした。

これがユニットテストの目的です。コラボレータークラスの使用方法に対するこの種の依存関係の影響を受けないテストは、実際にはサブシステムテストまたは統合テストです。もちろん、これらも頻繁にJUnitで記述されており、しばしばモックの使用を伴います。私の意見では、「JUnit」はひどい名前で、すべての異なるタイプのテストを作成できる製品のことです。

72

デイビッドの答えはもちろん正しいのですが、なぜあなたがこれを望むのかを十分には説明していません。

基本的に、ユニットテストを行うときは、機能ユニットを単独でテストします。入力が期待される出力を生成するかどうかをテストします。時には、副作用もテストする必要があります。簡単に言うと、verifyを使用すると、それが可能になります。

たとえば、DAOを使用して物事を保存することになっているビットのビジネスロジックがあります。これを行うには、DAOをインスタンス化し、ビジネスロジックに接続し、データベースを調べて、予想されるものが保存されているかどうかを確認する統合テストを使用します。それはもう単体テストではありません。

または、DAOをモックして、期待どおりに呼び出されることを確認できます。 mockitoを使用すると、何かが呼び出されること、呼び出される頻度、およびパラメーターでマッチャーを使用して、特定の方法で呼び出されることを確認できます。

このような単体テストの裏側は、実際、テストを実装に結び付けているため、リファクタリングが少し難しくなります。一方、良いデザインの匂いは、適切に実行するために必要なコードの量です。テストを非常に長くする必要がある場合は、おそらく設計に何らかの問題があります。そのため、テストする必要がある多くの副作用/複雑な相互作用を伴うコードは、おそらく良いものではありません。

55
Jilles van Gurp

これは素晴らしい質問です!根本的な原因は次のとおりだと思います。ユニットテストだけでなく、JUnitを使用しています。したがって、質問を分割する必要があります。

  • integration(または他のユニットテストよりも高い)テストでMockito.verify()を使用する必要がありますか?
  • black-box単体テストでMockito.verify()を使用する必要がありますか?
  • white-box単体テストでMockito.verify()を使用する必要がありますか?

ユニットテストよりも高いテストを無視する場合、質問は「Mockito.verifyでのwhite-boxユニットテストの使用」と言い換えることができます。 ()単体テストと実装の間に素晴らしいカップルを作成します。 "grey-box"単体テストとこれに使用する経験則」。

それでは、このすべてを段階的に見ていきましょう。

*-Mockito.verify()をintegration(または他の単体テストよりも高い)テストで使用する必要がありますか?*答えはもちろん、これにモックを使用するべきではありません。テストはできるだけ実際のアプリケーションに近いものにする必要があります。アプリケーションの孤立した部分ではなく、完全なユースケースをテストしています。

*black-boxvswhite-boxunit-testing*black-boxアプローチを使用している場合、実際に何をしているのかを指定します(すべての等価クラス)入力、state、および期待される出力を受け取ることをテストします。このアプローチでは、一般にモックを使用することは正当化されます(正しいことを実行していることを模倣するだけで、テストしたくない)が、Mockito.verify()を呼び出すことは不要です。

white-boxアプローチを実際に使用している場合は、behaviourをテストしていますあなたのユニットの。このアプローチでは、Mockito.verify()を呼び出すことが不可欠です。ユニットが期待どおりに動作することを確認する必要があります。

グレーボックステストの経験則ホワイトボックステストの問題は、高いカップリングを作成することです。可能な解決策の1つは、ホワイトボックステストではなく、グレーボックステストを実行することです。これは、白黒ボックステストの一種です。ホワイトボックスのテストのようにユニットの動作を実際にテストしていますが、一般的には実装に依存しません可能な場合。可能な場合は、ブラックボックスの場合と同様にチェックを行い、出力が予想どおりであることをアサートします。それで、あなたの質問の本質は、それが可能なときです。

これは本当に難しいです。良い例はありませんが、例を挙げましょう。 equals()とequalsIgnoreCase()を使用して上記で言及した場合、Mockito.verify()を呼び出して出力をアサートしないでください。できなかった場合は、できるまでコードを小さな単位に分解します。一方、@ Serviceがあり、本質的に@Serviceのラッパーである@ Web-Serviceを作成していると仮定します。これは、すべての呼び出しを@Serviceに委任します(さらにいくつかのエラー処理を行います)。この場合、Mockito.verify()の呼び出しは必須です。@ Seriveに対して行ったすべてのチェックを複製しないでください。正しいパラメーターメーターリストで@Serviceを呼び出していることを確認してください。

28
alexsmail

私はあなたが古典的なアプローチの観点から絶対に正しいと言わなければなりません:

  • 最初にアプリケーションのビジネスロジックを作成(または変更)し、次にテストでそれを(採用)テストTest-Last approach)、入力と出力のチェック以外に、ソフトウェアにどのように動作するかをテストに知らせるのは非常に苦痛で危険です。
  • Test-Driven approach を実践している場合、テストは 最初に記述され、変更され、反映されるユースケース ソフトウェアの機能。 実装はテストに依存します。それは時々、ソフトウェアを何らかの特定の方法で実装したいことを意味します。他のコンポーネントのメソッドに依存するか、特定の回数だけ呼び出すこともできます。それが Mockito.verify() が便利なところです!

普遍的なツールはないことを覚えておくことが重要です。ソフトウェアの種類、サイズ、会社の目標と市場の状況、チームのスキルなど、多くのことが、特定のケースで使用するアプローチの決定に影響します。

8
hammelion

一部の人々が言っ​​たように

  1. アサートできる直接出力がない場合があります
  2. 場合によっては、テストしたメソッドが正しい間接出力をそのコラボレーター(あなたがモックしている)に送信していることを確認する必要があるだけです。

リファクタリングの際にテストを壊すことについての懸念については、モック/スタブ/スパイを使用する場合にある程度予想されます。定義上ではなく、Mockitoなどの特定の実装に関するものではありません。しかし、このように考えることができます-メソッドの動作方法に大きな変更をもたらすリファクタリングを行う必要がある場合は、TDDアプローチで行うことをお勧めします。つまり、テストを変更できるということです最初は新しい動作(テストに失敗する)を定義し、thenは変更を行い、テストに再度合格します。