Mockito when()调用如何工作?


111

给出以下Mockito语句:

when(mock.method()).thenReturn(someValue);

假设嘲笑方法将返回值传递给when(),那么Mockito如何为该嘲笑创建代理?我想这会使用一些CGLib的东西,但是想知道如何从技术上做到这一点。

Answers:


118

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

几分钟后,对微型代码进行了简短的分析。注意,这是一个非常粗糙的描述-这里有很多细节。我建议您自己检查github上源代码

首先,当您使用类的mock方法模拟一个类时Mockito,实际上是这样:

  1. Mockito.mockorg.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)

这是此代码执行的顺序:

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

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

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


1
感谢您的详细回复。另一个问题-您提到“在method()调用之后何时调用方法”-如何知道when()的调用是下一次对method()的调用(或包装)?希望有道理。
marchaos

@marchaos不知道。使用when(mock.method()).thenXyz(...)语法,mock.method()将在“重放”模式下执行,而不是在“存根”模式下执行。通常情况下,该执行的mock.method()没有效果,所以后来当thenXyz(...)thenReturnthenThrowthenAnswer得到执行,等等),它进入“磕碰”模式,并且然后记录该方法调用所期望的结果。
罗杰里奥

1
Rogerio,实际上比这更微妙-Mockito没有显式的存根和重播模式。我将在以后编辑答案,这样更清楚。
Paul Morie 2013年

简而言之,用CGLIB或Javassist在另一个方法中拦截一个方法调用要比说“ if”运算符更容易。
Infeligo 2014年

我还没有在这里详细介绍我的描述,但是我也没有忘记它。仅供参考。
保罗·莫里

33

简短的答案是,在后台,Mockito使用某种全局变量/存储来保存方法存根构建步骤的信息(在您的示例中调用method(),when()和thenReturn()),以便最终可以建立关于在什么参数上调用什么应该返回什么的映射。

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

在我看来,这是反模式的典型用法。通常,我们在实现方法时应避免“副作用”,这意味着该方法应接受输入并进行一些计算并返回结果-除此之外,没有其他更改。但是Mockito只是故意违反了该规则。它的方法除了返回结果外,还存储大量信息:Mockito.anyString(),mockInstance.method(),when(),thenReturn,它们都有特殊的“副作用”。这就是框架乍一看看起来像魔术的原因-我们通常不写那样的代码。但是,在模拟框架的情况下,这种反模式设计是一个很好的设计,因为它可以导致非常简单的API。


4
优秀的链接。其背后的天才是:非常简单的API使整个事情看起来非常不错。另一个很好的决定是,when()方法使用泛型,因此thenReturn()方法是类型安全的。
David Tonhofer

我认为这是更好的答案。与其他答案相反,它清楚地解释了模拟过程的概念,而不是通过具体代码进行控制的流程。我同意,优秀的链接。
mihca
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.