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

如何使用Mockito与Spock测试Feature Toggle功能开关

解决方案

你遇到的测试失败是正常现象:当checksLoginAndRegistration=false时,代码不会进入service.validateRegistration的调用分支,自然不会抛出你预期的AccountBlockedException。对于Feature Toggle的测试,补充现有测试、单独编写用例两种方案都可行,更推荐后者保证测试逻辑的单一清晰。


方案1:独立编写两类测试用例(推荐)

为开关的两种状态分别写测试,逻辑直观易维护:

  • 开关开启场景:保留你原有测试的逻辑,显式指定配置feature.toggles.checksLoginAndRegistration=true,验证异常抛出、validateRegistration被调用
  • 开关关闭场景:显式指定配置feature.toggles.checksLoginAndRegistration=false,验证无异常抛出、validateRegistration未被调用

Spock测试示例(用ReflectionTestUtils直接赋值更轻量,无需加载全量Spring上下文)

def "开关开启时,注册校验不通过抛出账户锁定异常"() {
    given: "设置开关为开启状态"
    ReflectionTestUtils.setField(repository, "checksLoginAndRegistration", true)
    and: "mock validateRegistration抛出异常"
    1 * service.validateRegistration(_) >> { throw new AccountBlockedException(user.username) }
    // 其他依赖mock逻辑和原有测试保持一致即可

    when: "调用创建用户方法"
    repository.create(registrationData, anonymousInfo, "https://test")

    then: "捕获到预期异常"
    thrown(AccountBlockedException)
}

def "开关关闭时,跳过注册校验不抛出异常"() {
    given: "设置开关为关闭状态"
    ReflectionTestUtils.setField(repository, "checksLoginAndRegistration", false)
    and: "校验方法不会被调用"
    0 * service.validateRegistration(_) 
    // 其他依赖mock逻辑和原有测试保持一致即可

    when: "调用创建用户方法"
    def result = repository.create(registrationData, anonymousInfo, "https://test")

    then: "方法执行成功无异常"
    noExceptionThrown()
    result != null
}

方案2:参数化测试合并场景

如果希望减少重复代码,可以用Spock的参数化能力把两种场景合并为同一个测试方法:

def "开关状态为#toggleStatus时,注册校验逻辑符合预期"() {
    given: "设置开关状态为${toggleStatus}"
    ReflectionTestUtils.setField(repository, "checksLoginAndRegistration", toggleStatus)
    and: "mock对应依赖"
    invokeTimes * service.validateRegistration(_) >> { if(invokeTimes == 1) throw new AccountBlockedException(user.username) }
    // 其他依赖mock逻辑和原有测试保持一致即可

    when: "调用创建用户方法"
    repository.create(registrationData, anonymousInfo, "https://test")

    then: "结果符合预期"
    expectedException == null ? noExceptionThrown() : thrown(expectedException)

    where:
    toggleStatus | invokeTimes | expectedException
    true         | 1           | AccountBlockedException
    false        | 0           | null
}

注意要点

无论用哪种方案,都要同时验证两个点:

  • 对应分支的业务逻辑执行结果符合预期
  • 未命中的分支对应的依赖方法确实没有被调用,避免出现开关不生效的隐藏问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 08:12:03