次の Mockito ステートメントがあるとします。
when(mock.method()).thenReturn(someValue);
mock.method() ステートメントが when() に戻り値を渡すとすれば、Mockito はどのようにモックのプロキシを作成しますか? これはCGLibのものを使用していると思いますが、これが技術的にどのように行われるか知りたいです.
次の Mockito ステートメントがあるとします。
when(mock.method()).thenReturn(someValue);
mock.method() ステートメントが when() に戻り値を渡すとすれば、Mockito はどのようにモックのプロキシを作成しますか? これはCGLibのものを使用していると思いますが、これが技術的にどのように行われるか知りたいです.
簡単な答えは、あなたの例では、の結果はmock.method()
型に適した空の値になるということです。mockitoは、プロキシ、メソッドインターセプト、およびMockingProgress
クラスの共有インスタンスを介した間接参照を使用して、モックでのメソッドの呼び出しが、の戻り値を介してスタブに関する情報を渡すのではなく、既存のスタブ動作のスタブまたは再生のどちらであるかを判断します。模擬メソッド。
mockitoコードを調べた数分のミニ分析は次のとおりです。これは非常に大まかな説明であることに注意してください。ここには多くの詳細があります。githubのソースを自分でチェックすることをお勧めします。
mock
まず、クラスのメソッドを使用してクラスをモックすると、Mockito
基本的に次のようになります。
Mockito.mock
org.mockito.internal.MockitoCore
.mockに委任し、デフォルトのモック設定をパラメーターとして渡します。MockitoCore.mock
org.mockito.internal.util.MockUtil
.createMockに委任しますMockUtil
クラスはクラスを使用して、モックの作成に使用するClassPathLoader
インスタンスを取得します。MockMaker
デフォルトでは、CgLibMockMakerクラスが使用されます。CgLibMockMaker
ClassImposterizer
モックの作成を処理するJMockから借用したクラスを使用します。使用される「mockitomagic」の重要な部分はMethodInterceptor
、モックを作成するために使用されます。mockitoと、MockHandlerImplのインスタンスを含むMockHandlerMethodInterceptorFilter
インスタンスのチェーンです。メソッドインターセプターは、呼び出しをMockHandlerImplインスタンスに渡します。これは、メソッドがモックで呼び出されたときに適用されるビジネスロジックを実装します(つまり、回答が既に記録されているかどうかの検索、呼び出しが新しいスタブを表すかどうかの判断など)。デフォルトの状態では、呼び出されているメソッドにスタブがまだ登録されていない場合、タイプに適した空の値が返されます。次に、例のコードを見てみましょう。
when(mock.method()).thenReturn(someValue)
このコードが実行される順序は次のとおりです。
mock.method()
when(<result of step 1>)
<result of step 2>.thenReturn
何が起こっているかを理解するための鍵は、モック上のメソッドが呼び出されたときに何が起こるかです。メソッドインターセプターはメソッド呼び出しに関する情報を渡され、MockHandler
インスタンスのチェーンに委任され、最終的にはに委任されMockHandlerImpl#handle
ます。の間MockHandlerImpl#handle
に、モックハンドラーはのインスタンスを作成し、OngoingStubbingImpl
それを共有MockingProgress
インスタンスに渡します。
のwhen
呼び出し後にメソッドが呼び出されると、同じクラスのメソッドを呼び出すmethod()
に委任されます。このメソッドは、モックされた呼び出しが書き込んだ共有インスタンスから進行中のスタブをアンパックし、それを返します。次に、インスタンスでメソッドが呼び出されます。MockitoCore.when
stub()
MockingProgress
method()
thenReturn
OngoingStubbing
簡単な答えは、舞台裏で、Mockito はある種のグローバル変数/ストレージを使用して、メソッド スタブ構築手順 (この例では method()、when()、thenReturn() の呼び出し) の情報を保存するため、最終的にはどのパラメーターで何が呼び出されたときに何を返す必要があるかについてのマップを作成します。
この記事は非常に役に立ちました: プロキシ ベースのモック フレームワークの仕組みの説明( http://blog.rseiler.at/2014/06/explanation-how-proxy-based-mock.html )。著者はデモンストレーション モッキング フレームワークを実装しました。これは、これらのモッキング フレームワークがどのように機能するかを理解したい人にとって非常に優れたリソースであることがわかりました。
私の意見では、これはアンチパターンの典型的な使い方です。通常、メソッドを実装するときは「副作用」を避ける必要があります。つまり、メソッドは入力を受け入れ、何らかの計算を行い、結果を返す必要があります。それ以外は何も変更されていません。しかし、Mockito は意図的にそのルールに違反しています。そのメソッドは、結果を返すだけでなく、一連の情報を保存します: Mockito.anyString()、mockInstance.method()、when()、thenReturn、それらはすべて特別な「副作用」を持っています。これが、フレームワークが一見魔法のように見える理由でもあります。通常、私たちはそのようなコードを書きません。ただし、モッキング フレームワークの場合、このアンチパターン設計は非常に単純な API につながるため、優れた設計です。