6

Mockito の内部機能がどのように機能するかを理解しようとしています。これまでのところ、コードを理解するのは難しかったので、Mockito の基本的な仕組みについて高レベルの調査を探しています。

Mockito @ GrepCode

現在の理解を示すために、いくつかのサンプル コードを作成しました。

class C {
    String s;
    public void getS() { return s; }
    // ...
}

C cm = mock( C.class);
when( cm.method() ).thenReturn( "string value");

私が知る限り、「モック」メソッドは cm.getS() の戻り値しか見ていません。メソッドの名前を (スタブ化するために) どうすれば知ることができますか? また、メソッドに渡された引数をどのように知ることができますか?

mockito API メソッドは、内部オブジェクトのメソッドを呼び出します。

// org.mockito.Mockito
public static <T> OngoingStubbing<T> when(T methodCall) {
    return MOCKITO_CORE.when(methodCall);
}

メソッドの呼び出しをいくつかの異なる抽象化とクラスおよびオブジェクトにたどりましたが、コードが非常に分離されているため、この方法で理解するのは困難です。

//  org.mockito.internal.MockitoCore
public <T> OngoingStubbing<T> when(T methodCall) {
    mockingProgress.stubbingStarted();
    return (OngoingStubbing) stub();
}

したがって、誰かが内部構造を理解しているか、ディスカッション/ブログ投稿へのリンクを持っている場合は、共有してください:)

4

1 に答える 1

19

(長くなってすみません。TL;DR: Mockito は舞台裏でメソッド呼び出しを記録します。)

C cm = mock(C.class);

cmこの時点で、これは ... のインスタンスだと思うかもしれませんが、それはC間違いです。代わりに、実装する(したがって、C 型のフィールド/変数に割り当てることができる)プロキシ オブジェクトwritescmのインスタンスですが、要求したすべてを記録し、スタブした方法で動作します。 MockitoC

モック クラスを手動で作成してみましょう...そしてもう 1 つのメソッド、たとえば を与えて、実際のクラスにとint add(int a, int b)を追加します。ab

class MockC extends C {
  int returnValue;

  @Override int add(int a, int b) {
    return returnValue;
  }
}

そこには!これで、を呼び出すたびaddに、2 つの数値が加算されず、代わりに 1 つの戻り値が返されます。理にかなっています。しかし、後で呼び出しを確認したい場合はどうすればよいでしょうか?

class MockC extends C {
  List<Object[]> parameterValues = new ArrayList<>();
  int returnValue;

  @Override int add(int a, int b) {
    parameterValues.add(new Object[] { a, b });
    return returnValue;
  }
}

これで、parameterValuesリストをチェックして、期待どおりに呼び出されたことを確認できます。

Mockito は、MockC のように自動的に動作するCGLIBを使用してプロキシを生成し、すべての相互作用と戻り値を 1 つの大きな静的リストに保持します。リストは と呼ばれ、すべてのモックのすべてのメソッド呼び出しRegisteredInvocationsの代わりに が呼び出されますが、考え方は同じです。Object[]Invocation

RegisteredInvocations公開されているメソッドとその重要性についてもう少し理解するremoveLastには、 のコードを読んでくださいInvocationContainer。Mockito はすべての通話を記録するため、 に含まれる対話を自然に記録しますwhen。Mockito が を確認するとすぐに、最後に記録されたインタラクション( InvocationContainerImpl.javawhenの 45 行目) を削除し、それをスタブのテンプレートとして使用して、 Invocation オブジェクト自体から引数値を読み取ります。

eqとのような引数マッチャーを除いて、それはほとんどのことをany処理しますArgumentMatcherStorage。このwhen呼び出しは、スタック上にいくつのマッチャーがあるかをチェックします。たとえば、addマッチャーがゼロの場合、Mockito は記録されたすべての引数と同等であると推測し、2 つのマッチャーはそれらをスタックからポップして使用するように Mockito に指示します。マッチャーが 1 つあるだけで、Mockito はどの整数と一致させようとしているのかを判断できず、しばしば混乱を招くInvalidUseOfMatchersException.

それが役立つことを願っています!

編集: この回答では、メソッドがどのように機能するかwhenをより技術的に詳しく説明しています。

于 2013-03-29T06:31:20.930 に答える