Spock模拟CustomException异常致JaCoCo代码覆盖率未覆盖if条件
问题根源
全局GroovyMock靠字节码篡改拦截CustomException的构造过程,这会导致JaCoCo无法正确追踪Validator类中if (environment == null || environment.isEmpty())分支的执行路径——虽然测试能正常跑通(说明分支确实被进入了),但Groovy的Mock机制绕开了JaCoCo插桩的字节码,使得覆盖率报告没把这个分支标记为已覆盖。
解决办法
放弃全局GroovyMock,改用Spock的普通Mock或Spy来模拟CustomException的方法,确保Validator类的原字节码执行路径能被JaCoCo正确捕捉。
具体操作
方式1:用Spy保留原类执行路径(无需修改业务代码)
通过全局Mock仅替换CustomException的构造函数,返回一个Spy实例(Spy会保留原类的字节码逻辑),让JaCoCo能统计到分支执行情况:
class ValidatorSpec extends Specification { def "test env check with null environment"() { given: // 创建Spy实例,保留原类行为 CustomException ceSpy = Spy(CustomException) // 全局Mock构造函数,返回Spy GroovyMock(CustomException, global: true) new CustomException() >> ceSpy // 定义Spy的方法抛出异常 ceSpy.errorThrowException() >> { throw new IllegalStateException() } when: new Validator().envCheck(null) then: thrown(IllegalStateException) // 验证方法被调用 1 * ceSpy.errorThrowException() } }
方式2:改为依赖注入(更推荐,代码更易测试)
调整业务代码,将CustomException通过构造注入的方式传入Validator,测试时直接传入Mock对象,完全避免全局Mock:
修改后的Validator类
public class Validator { private final CustomException customException; // 构造注入CustomException public Validator(CustomException customException) { this.customException = customException; } public void envCheck(String environment) { if (environment == null || environment.isEmpty()) { customException.errorThrowException(); } } }
对应的Spock测试代码
class ValidatorSpec extends Specification { def "test env check with null environment"() { given: // 创建普通Mock对象 CustomException ceMock = Mock(CustomException) ceMock.errorThrowException() >> { throw new IllegalStateException() } Validator validator = new Validator(ceMock) when: validator.envCheck(null) then: thrown(IllegalStateException) 1 * ceMock.errorThrowException() } }
验证结果
修改后重新运行测试并生成JaCoCo报告,if (environment == null || environment.isEmpty())分支会被标记为已覆盖,同时测试逻辑依然正常执行。
内容的提问来源于stack exchange,提问作者Umesh Kumar
相关产品推荐
相关产品推荐

