mockito when()呼び出しはどのように機能しますか?


111

次のMockitoステートメントがあるとします。

when(mock.method()).thenReturn(someValue);

mock.method()ステートメントが戻り値をwhen()に渡す場合、Mockitoはモックのプロキシ処理をどのように作成しますか?私はこれがいくつかのCGLibのものを使用することを想像しますが、これが技術的にどのように行われるかを知りたいと思います。

回答:


118

短い答えは、あなたの例では、結果はmock.method()型に適切な空の値になるということです。mockitoは、プロキシー、メソッドインターセプト、およびMockingProgressクラスの共有インスタンスを介した間接参照を使用して、モック上のメソッドの呼び出しが、スタブに関する情報を、モックメソッド。

mockitoコードを見て数分でミニ分析は次のとおりです。これは非常に大まかな説明です。多くの詳細がここにあります。githubソースを自分でチェックすることをお勧めします。

まず、クラスのmockメソッドを使用してクラスをモックすると、Mockito基本的にこれが起こります。

  1. Mockito.mock 代議員 org.mockito.internal.MockitoCore.mockに、デフォルトのモック設定をパラメーターとして渡します。
  2. MockitoCore.mock 代議員 org.mockito.internal.util.MockUtil.createMockデリゲート
  3. MockUtilクラスには、使用ClassPathLoaderのインスタンスを取得するクラスをMockMakerモックを作成するために使用するが。デフォルトでは、CgLibMockMakerクラスが使用されます。
  4. CgLibMockMakerJMockから借用したクラスを使用してClassImposterizer、モックの作成を処理します。使用「mockitoマジック」のキー片がされているMethodInterceptormockito:モック作成するために使用されるMethodInterceptorFilter、とのインスタンスを含むMockHandlerインスタンスのチェーン、MockHandlerImplを。メソッドインターセプターはMockHandlerImplインスタンスに呼び出しを渡します。MockHandlerImplインスタンスは、メソッドがモックで呼び出されたときに適用されるビジネスロジックを実装します(つまり、回答が既に記録されているかどうかを検索し、呼び出しが新しいスタブを表すかどうかを判断します)。デフォルトの状態では、呼び出されるメソッドのスタブがまだ登録されていない場合、型に応じた空の値が返されます。

次に、例のコードを見てみましょう。

when(mock.method()).thenReturn(someValue)

このコードが実行される順序は次のとおりです。

  1. mock.method()
  2. when(<result of step 1>)
  3. <result of step 2>.thenReturn

何が起こっているのかを理解するための鍵は、モックのメソッドが呼び出されたときに何が起こるかです。メソッドインターセプターには、メソッド呼び出しに関する情報が渡され、MockHandlerインスタンスのチェーンに委任され、最終的にに委任されMockHandlerImpl#handleます。の間MockHandlerImpl#handleに、モックハンドラーはのインスタンスを作成し、OngoingStubbingImplそれを共有MockingProgressインスタンスに渡します。

ときwhenの方法は、の呼び出し後に呼び出されるmethod()と、それ委譲し、MockitoCore.when呼び出して、stub()同じクラスのメソッドを。このメソッドMockingProgressは、モックされたmethod()呼び出しが書き込まれた共有インスタンスから進行中のスタブをアンパックし、それを返します。次にthenReturnOngoingStubbingインスタンスでメソッドが呼び出されます。


1
詳しい回答ありがとうございます。別の質問-「method()の呼び出し後にwhenメソッドが呼び出される」とおっしゃっています-when()の呼び出しがmethod()の呼び出しの次の呼び出し(またはラップ)であることをどのようにして知るのですか?それが理にかなっていると思います。
marchaos 2013年

@marchaosわかりません。when(mock.method()).thenXyz(...)構文、mock.method()ない「スタブ」モードでは、「リプレイ」モードで実行されます。通常、この実行mock.method()は効果がありませんが、そう後でときthenXyz(...)thenReturnthenThrowthenAnswer、など)が実行されます、それは「スタブ」モードに入ると、そのメソッド呼び出しのために望ましい結果を記録します。
ホジェリオ

1
ロジェリオ、実際にはそれよりも少し微妙です。mockitoには明示的なスタブおよび再生モードがありません。後で回答を編集して、わかりやすくします。
ポール・森江

要するに、CGLIBまたはJavassistを使用して別のメソッド内のメソッド呼び出しをインターセプトする方が、「if」演算子などをインターセプトする方が簡単です。
Infeligo 2014

私はまだここで自分の説明をマッサージしていませんが、それについても忘れていません。ご参考までに。
Paul Morie、2015年

33

短い答えは、裏では、Mockitoはある種のグローバル変数/ストレージを使用して、メソッドスタブのビルドステップ(例ではmethod()、when()、thenReturn()の呼び出し)の情報を保存するため、最終的にはどのパラメータで何が呼び出されたときに何が返されるかについてのマップを作成します。

この記事はとても役に立ちました: プロキシベースのモックフレームワークの動作の説明http://blog.rseiler.at/2014/06/explanation-how-proxy-based-mock.html)。著者はデモンストレーションMockingフレームワークを実装しました。これは、これらのMockingフレームワークがどのように機能するかを理解したい人にとって、非常に優れたリソースです。

私の意見では、これはアンチパターンの典型的な使用法です。通常、メソッドを実装するときは「副作用」を回避する必要があります。つまり、メソッドは入力を受け入れて何らかの計算を行い、結果を返す必要があります。それ以外は何も変更されません。しかし、モッキトはわざとそのルールに違反しています。そのメソッドは、結果を返す以外に、Mockito.anyString()、mockInstance.method()、when()、thenReturnなどの一連の情報を格納します。これらはすべて特別な「副作用」があります。そのため、フレームワークは一見すると魔法のように見えます。通常、そのようなコードは記述しません。ただし、モックフレームワークの場合、このアンチパターンデザインは非常にシンプルなAPIにつながるため、優れたデザインです。


4
優れたリンク。この背後にある天才は、全体を非常に素晴らしく見えるようにする非常にシンプルなAPIです。別のすばらしい決定は、when()メソッドがジェネリックスを使用するため、thenReturn()メソッドがタイプセーフになるということです。
David Tonhofer 2017年

私はこれがより良い答えだと思います。他の回答とは対照的に、具体的なコードによる制御フローではなく、モッキングプロセスの概念を明確に説明しています。私は同意します、優れたリンク。
mihca
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.