如何验证传入Mock对象方法的闭包内代码行为?
闭包依赖的Mock测试解决方案
问题场景
在Jenkins共享库开发中,很多流水线步骤以接收闭包的形式提供功能。测试这类代码时,我们通常会Mock Jenkins相关依赖,但会遇到两个核心问题:
- 被测方法中传给Mock依赖的闭包不会自动执行,导致闭包内调用的其他依赖方法无法验证
- 无法通过匹配闭包内容来验证第一个Mock依赖的调用是否符合预期
泛化后场景:被测类包含两个依赖,被测方法先准备数据,再将包含第二个依赖调用的闭包传入第一个依赖的方法。由于第一个依赖被Mock,闭包不会执行,既没法验证第二个依赖的调用,也没法通过闭包匹配验证第一个依赖的调用。
相关代码
被测类代码
class SystemUnderTest { DependencyOne firstDependency DependencyTwo secondDependency public SystemUnderTest(DependencyOne firstDependency, DependencyTwo secondDependency) { this.firstDependency = firstDependency this.secondDependency = secondDependency } public void methodUnderTest(Integer a, Integer b) { Integer sum = a+b firstDependency.methodWhichTakesClosure() { // 需要验证的核心逻辑 secondDependency.useResult(sum) } } }
依赖类代码
DependencyOne(对应Jenkins流水线步骤类)
class DependencyOne { public methodWhichTakesClosure(Closure c) { // 包含外部副作用的逻辑,比如调用Jenkins流水线API c.call() } }
DependencyTwo(业务逻辑依赖)
class DependencyTwo { public void useResult(Integer i) { // 处理结果的业务逻辑 println(i) } }
初始测试代码(存在问题)
@RunWith(MockitoJUnitRunner.class) class TestClass { SystemUnderTest sut @Mock DependencyOne firstDependency @Mock DependencyTwo secondDependency @Before void setup() { sut = new SystemUnderTest(firstDependency, secondDependency) } @Test void verify_methodUnderTest() { sut.methodUnderTest(1,2) // 尝试1:firstDependency是Mock,闭包未执行,验证永远失败 verify(secondDependency).useResult(3) // 尝试2:闭包是匿名对象,无法匹配,验证失败 verify(firstDependency).methodWhichTakesClosure { secondDependency.useResult(3) } } }
解决办法:捕获闭包并手动执行
使用Mockito的ArgumentCaptor捕获传入第一个依赖的闭包参数,手动执行闭包触发内部逻辑,即可正常验证两个依赖的调用行为。
修改后的测试代码
@RunWith(MockitoJUnitRunner.class) class TestClass { SystemUnderTest sut @Mock DependencyOne firstDependency @Mock DependencyTwo secondDependency // 定义闭包类型的参数捕获器 @Captor ArgumentCaptor<Closure> closureCaptor @Before void setup() { sut = new SystemUnderTest(firstDependency, secondDependency) } @Test void verify_methodUnderTest() { // 执行被测方法 sut.methodUnderTest(1,2) // 1. 验证第一个依赖的方法被调用,并捕获传入的闭包 verify(firstDependency).methodWhichTakesClosure(closureCaptor.capture()) // 2. 手动执行捕获到的闭包,触发内部逻辑 Closure capturedClosure = closureCaptor.getValue() capturedClosure.call() // 3. 验证第二个依赖的方法是否被正确调用 verify(secondDependency).useResult(3) } }
逻辑说明
- 捕获闭包:通过
ArgumentCaptor<Closure>获取被测方法中传入第一个依赖的闭包对象 - 手动执行闭包:Mock的依赖不会自动执行闭包,手动调用
call()触发闭包内的业务逻辑 - 验证依赖调用:闭包执行后,第二个依赖的方法会被调用,此时可正常验证其调用参数和次数
如果需要验证第一个依赖的调用次数,直接在捕获闭包的步骤即可完成,无需匹配闭包内容。
内容的提问来源于stack exchange,提问作者qds
相关产品推荐
相关产品推荐

