You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何验证传入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)
    }
}

逻辑说明

  1. 捕获闭包:通过ArgumentCaptor<Closure>获取被测方法中传入第一个依赖的闭包对象
  2. 手动执行闭包:Mock的依赖不会自动执行闭包,手动调用call()触发闭包内的业务逻辑
  3. 验证依赖调用:闭包执行后,第二个依赖的方法会被调用,此时可正常验证其调用参数和次数

如果需要验证第一个依赖的调用次数,直接在捕获闭包的步骤即可完成,无需匹配闭包内容。

内容的提问来源于stack exchange,提问作者qds

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 20:36:38