【问题标题】:Mockito InternalsMockito 内部结构
【发布时间】:2013-03-19 13:35:51
【问题描述】:

我试图了解 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();
}

因此,如果有人了解内部结构或有讨论/博客文章的链接,请分享:)

【问题讨论】:

    标签: unit-testing testing mocking mockito internals


    【解决方案1】:

    (抱歉,这太长了。TL;DR:Mockito 在幕后记录方法调用。)

    C cm = mock(C.class);
    

    此时,您可能会认为cmC 的一个实例...您就错了。相反,cmproxy object 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,将所有交互和返回值保存在一个大的静态列表中。该列表称为 @987654324 @,而不是Object[],每个模拟的每个方法调用都是Invocation,但想法是一样的。

    要了解更多关于RegisteredInvocations 以及为什么它公开的removeLast 方法如此重要,请阅读InvocationContainer 中的代码。因为 Mockito 会记录每一次通话,它自然会记录 when 中包含的交互。一旦 Mockito 看到 when,它删除最后记录的交互InvocationContainerImpl.java 第 45 行)并将其用作存根的模板——从 Invocation 对象本身读取参数值。

    这可以解决大部分问题,除了 eqany 之类的参数匹配器:原来那些只是 kept on a fancy stack 称为 ArgumentMatcherStoragewhen 调用 checks how many matchers are on the stack:对于 add 示例,零匹配器告诉 Mockito 推断与每个记录的参数相等,两个匹配器告诉 Mockito 将它们从堆栈中弹出并使用它们。只有一个匹配器意味着 Mockito 无法判断您要匹配哪个整数并抛出经常令人困惑的InvalidUseOfMatchersException,这就是为什么在使用匹配器时,如果您匹配任何参数,您需要匹配每个参数。

    希望有帮助!

    编辑:This answer 更详细地描述了when 方法的工作原理。

    【讨论】:

      猜你喜欢
      • 2012-12-12
      • 1970-01-01
      • 2019-01-08
      • 2021-07-12
      • 1970-01-01
      • 1970-01-01
      • 2020-07-02
      • 2011-06-21
      • 1970-01-01
      相关资源
      最近更新 更多