mockito when()调用如何工作?

mar*_*aos 110 unit-testing mocking mockito

鉴于以下Mockito声明:

when(mock.method()).thenReturn(someValue);
Run Code Online (Sandbox Code Playgroud)

考虑到mock.method()语句将返回值传递给when(),Mockito如何为mock创建代理?我想这会使用一些CGLib的东西,但有兴趣知道这是如何在技术上完成的.

Pau*_*rie 112

简短的回答是,在您的示例中,结果mock.method()将是类型适当的空值; mockito通过代理,方法拦截和类的共享实例使用间接,MockingProgress以确定模拟方法的调用是用于存根还是重放现有的存根行为,而不是通过返回值传递有关存根的信息.一种嘲弄的方法.

在几分钟内查看mockito代码的迷你分析如下.请注意,这是一个非常粗略的描述 - 这里有很多细节.我建议你自己查看github上的源代码.

首先,当你使用类的mock方法模拟一个类时Mockito,这基本上是发生的事情:

  1. Mockito.mock委托给org.mockito.internal.MockitoCore.mock,将默认模拟设置作为参数传递.
  2. MockitoCore.mock委托给org.mockito.internal.util.MockUtil.createMock
  3. 本MockUtil类使用ClassPathLoader类来获得的实例MockMaker用于创建模拟.默认情况下,使用CgLibMockMaker类.
  4. CgLibMockMaker使用从JMock借来的类ClassImposterizer来处理创建模拟.所使用的"魔术的Mockito"键片的MethodInterceptor用于创建模拟:所述的Mockito MethodInterceptorFilter以及MockHandler实例,包括的实例的链MockHandlerImpl.方法拦截器将调用传递给MockHandlerImpl实例,该实例实现了在模拟上调用方法时应该应用的业务逻辑(即,搜索是否已经记录了答案,确定调用是否代表新存根等).默认状态是,如果尚未为要调用的方法注册存根,则返回适合类型的空值.

现在,让我们看一下示例中的代码:

when(mock.method()).thenReturn(someValue)
Run Code Online (Sandbox Code Playgroud)

以下是此代码将执行的顺序:

  1. mock.method()
  2. when(<result of step 1>)
  3. <result of step 2>.thenReturn

理解正在发生的事情的关键是调用mock上的方法时会发生什么:方法拦截器传递有关方法调用的信息,并委托给MockHandler最终委托给它的实例链MockHandlerImpl#handle.在此期间MockHandlerImpl#handle,模拟处理程序创建一个实例OngoingStubbingImpl并将其传递给共享MockingProgress实例.

在when调用之后调用该方法时method(),它会委托给MockitoCore.when,调用stub()同一个类的方法.此方法从MockingProgress模拟method()调用写入的共享实例中解压缩正在进行的存根,并将其返回.然后thenReturn在OngoingStubbing实例上调用方法.


Jac*_* Wu 29

简短的回答是,在幕后,Mockito使用某种全局变量/存储来保存方法存根构建步骤的信息(调用method(),when(),thenReturn()在你的例子中),这样它最终可以建立一个关于在什么参数上调用什么时应该返回的地图.

我发现这篇文章非常有用: 解释基于代理的Mock框架是如何工作的(http://blog.rseiler.at/2014/06/explanation-how-proxy-based-mock.html).作者实现了一个演示Mocking框架,我发现这是一个非常好的资源,可以帮助那些想要弄清楚这些Mocking框架如何工作的人.

在我看来,这是反模式的典型用法.通常我们应该在实现方法时避免"副作用",这意味着该方法应该接受输入并进行一些计算并返回结果 - 除此之外没有其他任何改变.但莫基托故意违反了这条规则.它的方法存储了一堆信息,除了返回结果:Mockito.anyString(),mockInstance.method(),when(),thenReturn,它们都有特殊的"副作用".这也是为什么框架在第一眼看起来像魔术一样 - 我们通常不会编写类似的代码.但是,在模拟框架的情况下,这种反模式设计是一个很好的设计,因为它导致非常简单的API.

  • 优秀的链接._这背后的天才是:非常简单的API,使整个事情看起来非常好.另一个重要决定是when()方法使用泛型,以便thenReturn()方法是类型安全的._ (3认同)
  • 我认为这是更好的答案。与其他答案相反,它清楚地解释了模拟过程的概念,而不是通过具体代码的控制流。我同意,很棒的链接。 (2认同)