Mockito 内部机制

7

我试图了解Mockito的内部功能。到目前为止,这段代码对我来说很难理解,我希望能够对Mockito的基本工作原理进行高层次的概述。

Mockito @ GrepCode

我编写了一些示例代码以演示我的当前理解:

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

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

据我所知,“mock”方法只查看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();
}

如果有人了解内部机制或者有相关的讨论/博客链接,请分享 :)
1个回答

20
抱歉这段内容有些冗长。简单来说,Mockito会在后台记录方法调用。
C cm = mock(C.class);

这时候,你可能会认为cmC的一个实例......但你错了。相反,cm是一个代理对象,由Mockito编写,实现了C(因此可以分配给类型C的字段/变量),但记录了你请求的所有内容,并按照你所设定的方式进行操作。

让我们手动编写一个模拟类......并再添加一个方法,比如说int add(int a, int b),它在实际类中将ab相加。

class MockC extends C {
  int returnValue;

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

现在,每当你调用add时,它不会将两个数字相加,而是只返回一个单一的返回值。很有道理。但是如果您想稍后验证这些调用呢?

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使用CGLIB生成proxy自动充当MockC,将所有交互和返回值保存在一个大的静态列表中。该列表称为RegisteredInvocations,每个模拟对象的每个方法调用都是Invocation,但思想是相同的。

为了更好地理解RegisteredInvocations以及它所暴露的removeLast方法的重要性,请查看InvocationContainer中的代码。因为Mockito记录每次调用,自然也会记录在when中包含的交互。一旦Mockito看到when,它就会移除最后一次记录的交互InvocationContainerImpl.java第45行),并将其用作您的存根的模板——从Invocation对象本身读取参数值。

大部分内容问题已经解决,但是如eqany这样的参数匹配器还需要处理:原来它们只是存在一个精致栈中,称为ArgumentMatcherStorage。对于when调用,检查栈上有多少个匹配器:对于add示例,零个匹配器告诉Mockito要对每个记录的参数推断相等性,而两个匹配器告诉Mockito要弹出并使用它们。只有一个匹配器意味着Mockito无法确定您要匹配哪个整数,并抛出常常令人困惑的异常InvalidUseOfMatchersException,因此在使用匹配器时,如果要匹配任何一个参数,则需要匹配每个参数。

希望能对您有所帮助! 编辑: 本答案更详细地描述了when方法的工作原理。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接