Unit testing mockito when()调用如何工作?
鉴于以下Mockito陈述:Unit testing mockito when()调用如何工作?,unit-testing,mocking,mockito,Unit Testing,Mocking,Mockito,鉴于以下Mockito陈述: when(mock.method()).thenReturn(someValue); 如果mock.method()语句将返回值传递给when(),Mockito如何为mock创建代理?我想这会用到一些CGLib的东西,但我很想知道这在技术上是如何做到的 简单的回答是,在您的示例中,mock.method()的结果将是一个类型适当的空值;mockito通过代理、方法截取和MockingProgress类的共享实例使用间接寻址,以确定在模拟上调用方法是用于存根还是重
when(mock.method()).thenReturn(someValue);
如果mock.method()语句将返回值传递给when(),Mockito如何为mock创建代理?我想这会用到一些CGLib的东西,但我很想知道这在技术上是如何做到的 简单的回答是,在您的示例中,
mock.method()
的结果将是一个类型适当的空值;mockito通过代理、方法截取和MockingProgress
类的共享实例使用间接寻址,以确定在模拟上调用方法是用于存根还是重放现有存根行为,而不是通过模拟方法的返回值传递有关存根的信息
几分钟后对mockito代码进行的一个小型分析如下。注意,这是一个非常粗略的描述-这里有很多细节。我建议你自己检查一下
首先,当您使用Mockito
类的mock
方法模拟一个类时,基本上会发生以下情况:
Mockito.mock
委托给.mock,将默认的mock设置作为参数传递MockitoCore.mock
委托给.createMockMockUtil
类使用ClassPathLoader
类获取用于创建模拟的MockMaker
实例。默认情况下,将使用该类CgLibMockMaker
使用从JMock借用的类,该类负责创建模拟。使用的“mockito魔术”的关键部分是用于创建模拟的MethodInterceptor
:mockitoMethodInterceptorFilter
,以及一系列MockHandler实例,包括的实例。方法拦截器将调用传递给MockHandlerImpl实例,该实例实现了在模拟上调用方法时应应用的业务逻辑(即,搜索以查看是否已记录答案,确定调用是否表示新存根等。默认状态是,如果存根尚未为所调用的方法注册,则返回与类型相应的空值when(mock.method()).thenReturn(someValue)
以下是此代码的执行顺序:
mock.method()
when()
。然后返回
MockHandler
实例链,这些实例最终委托给MockHandlerImpl#handle
。在MockHandlerImpl#handle
期间>,模拟处理程序创建ongoingstubingimpl
的实例,并将其传递给共享的MockingProgress
实例
调用
method()
后调用When
方法时,它将委托给MockitoCore。When
,它调用同一类的方法。此方法从被模拟的MockingProgress
方法()的共享MockingProgress
实例中解压正在进行的存根调用写入并返回它。然后对OngoingStubing
实例调用然后返回
方法。简短的回答是,在幕后,Mockito使用某种全局变量/存储来保存方法存根构建步骤的信息(调用方法(),when(),然后返回()在您的示例中),这样最终它就可以建立一个映射,当调用what param时应该返回什么
我发现这篇文章非常有用:
解释基于代理的模拟框架如何工作()。
作者实现了一个演示模拟框架,我发现这是一个非常好的资源,适合那些想了解这些模拟框架如何工作的人
在我看来,这是反模式的典型用法。通常我们在实现一个方法时应该避免“副作用”,这意味着该方法应该接受输入并进行一些计算并返回结果——除此之外,没有其他改变。但Mockito只是故意违反了这一规则。它的方法除了返回结果之外还存储了一堆信息结果:Mockito.anyString(),mockInstance.method(),when(),然后返回,它们都有特殊的“副作用”。这也是为什么框架乍看起来像一个魔术——我们通常不会编写这样的代码。然而,在模拟框架的情况下,这种反模式设计是一个伟大的设计,因为它导致了非常简单的API。感谢您的详细回答。另一个问题——您提到了that“调用方法()后调用方法的时间”-它如何知道调用方法()的时间()是调用方法()的下一个调用(或包装)?希望这是有意义的。@marchaos它不知道。使用
when(mock.method()).thenXyz(…)
语法,mock.method()
在“replay”中执行模式,而不是“stubing”模式。通常情况下,mock.method()
的执行没有效果,因此稍后当执行thenXyz(…)
(thenReturn
,thenThrow
,thenAnswer
等)时,它进入“stubing”状态模式,然后记录该方法调用所需的结果。Rogerio,实际上比这更微妙-mockito没有显式的存根和重播模式。我稍后将编辑我的答案,以便更清楚。总之,使用CGLIB或Javassist在另一个方法中拦截方法调用更容易,因为它可以进行交互ept,比如“if”操作符。我还没有在这里修改我的描述,但我也没有忘记它。仅供参考。非常好的链接。这背后的天才是:非常简单的API,使整个事情看起来非常好。另一个伟大的决定是when()方法使用gener