给定以下 Mockito 语句:
when(mock.method()).thenReturn(someValue);
当使用mockito中的mock对象调用mock.method()方法时,mockito是如何创建一个代理对象,并将返回值传递给when()方法的呢?我想这可能涉及到一些CGLib的东西,但我很想知道这是如何实现的。
给定以下 Mockito 语句:
when(mock.method()).thenReturn(someValue);
当使用mockito中的mock对象调用mock.method()方法时,mockito是如何创建一个代理对象,并将返回值传递给when()方法的呢?我想这可能涉及到一些CGLib的东西,但我很想知道这是如何实现的。
mock.method()
的结果将是一个类型相应的空值;Mockito 使用代理、方法拦截和共享的 MockingProgress
类的实例来确定对模拟对象方法的调用是用于存根化还是重播已经存在的存根化行为,而不是通过模拟方法的返回值传递有关存根信息。Mockito
类的mock
方法进行类模拟时,实际上会发生以下情况: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 实例,MockHandlerImpl 实现了应在模拟对象上调用方法时应用的业务逻辑(即搜索查看是否已记录答案,确定调用是否表示新的存根等)。默认状态是,如果尚未为正在调用的方法注册存根,则返回类型相应的空值。when(mock.method()).thenReturn(someValue)
此代码的执行顺序如下:
mock.method()
when(<step 1 的结果>)
<step 2 的结果>.thenReturn
理解此过程的关键在于对 mock 方法被调用时发生的情况有所了解:方法拦截器将接收有关方法调用的信息,并委托给其 MockHandler
实例链,这些实例最终会委托给 MockHandlerImpl#handle
. 在 MockHandlerImpl#handle
过程中,mock 处理程序创建了一个 OngoingStubbingImpl
实例,并将其传递给共享的 MockingProgress
实例。
当在调用 method()
之后再调用 when
方法时,它会委托给 MockitoCore.when
,该方法会调用相同类的stub()
方法。该方法从被模拟的 method()
调用写入的共享的 MockingProgress
实例中拆分 ongoing stubbing,并返回它。然后在 OngoingStubbing
实例上调用 thenReturn
方法。
when(mock.method()).thenXyz(...)
语法时,mock.method()
在“回放”模式下执行,而不是在“存根”模式下执行。通常,此执行mock.method()
没有任何效果,因此稍后当执行thenXyz(...)
(thenReturn
,thenThrow
,thenAnswer
等) 时,它进入“存根”模式,然后记录该方法调用的所需结果。 - RogérioMockito.when()
需要惰性评估其参数才能正常工作。调用mock.method()
将被暂停并传递给Mockito.when()
,然后Mockito.when()
需要检查挂起的操作,发现mock
实例和method
方法调用的名称,然后设置即将到来的method()
期望响应的定义。JVM 需要几个功能和运行时代码内省特性,它没有这些功能。但是通过具有静态的MockingProgress
管理元素来解决了所有问题。 - David Tonhofer