web-dev-qa-db-ja.com

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

次のMockitoステートメントを考えます。

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

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

105
marchaos

簡単な答えは、あなたの例では、mock.method()の結果は型に適した空の値になるということです。 mockitoは、プロキシ、メソッドインターセプト、およびMockingProgressクラスの共有インスタンスを介したインダイレクションを使用して、戻り値を介してスタブに関する情報を渡すのではなく、モックでのメソッドの呼び出しがスタブまたは既存のスタブ動作の再生であるかどうかを判断しますモックメソッドの.

モッキートのコードを数分で見るミニ分析は次のとおりです。これは非常に大雑把な説明であることに注意してください-ここには多くの詳細があります。 githubのソース を自分でチェックアウトすることをお勧めします。

最初に、mockクラスのMockitoメソッドを使用してクラスをモックすると、基本的には次のようになります。

  1. Mockito.mockorg.mockito.internal.MockitoCore 。mockに委任し、デフォルトのモック設定をパラメーターとして渡します。
  2. MockitoCore.mockorg.mockito.internal.util.MockUtil 。createMockに委任します
  3. MockUtilクラスはClassPathLoaderクラスを使用して、モックの作成に使用するMockMakerのインスタンスを取得します。デフォルトでは、 CgLibMockMaker クラスが使用されます。
  4. CgLibMockMakerは、モックの作成を処理するJMock ClassImposterizer から借用したクラスを使用します。使用される「mockitoマジック」の重要な部分は、モックの作成に使用されるMethodInterceptor、mockito MethodInterceptorFilter、および MockHandlerImpl のインスタンスを含むMockHandlerインスタンスのチェーンです。メソッドインターセプターは、MockHandlerImplインスタンスに呼び出しを渡します。このインスタンスは、モックでメソッドが呼び出されたときに適用されるビジネスロジックを実装します(つまり、回答が既に記録されているかどうかを検索したり、呼び出しが新しいスタブを表すかどうかを判断したりするなど)。デフォルトの状態では、呼び出されるメソッドに対してスタブがまだ登録されていない場合、タイプに適したempty値が返されます。

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

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インスタンスに渡します。

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

108
Paul Morie

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

この記事は非常に役に立ちました:プロキシベースのMock Frameworksの仕組みの説明(- http://blog.rseiler.at/2014/06 /explanation-how-proxy-based-mock.html )。著者はデモ用のモックフレームワークを実装しました。これは、これらのモックフレームワークがどのように機能するかを知りたい人にとって非常に良いリソースです。

私の意見では、それはアンチパターンの典型的な使用法です。通常、メソッドを実装するときは「副作用」を避ける必要があります。つまり、メソッドは入力を受け入れて計算を行い、結果を返す必要があります。それ以外の変更はありません。しかし、Mockitoは故意にその規則に違反しているだけです。そのメソッドは、結果を返すだけでなく、Mockito.anyString()、mockInstance.method()、when()、thenReturnのような多くの情報を格納します。これらはすべて、特別な「副作用」があります。また、フレームワークが一見魔法のように見える理由でもあります。通常、このようなコードは作成しません。ただし、モックフレームワークの場合、このアンチパターン設計は非常に単純なAPIにつながるため、素晴らしい設計です。

26
Jacob Wu