浅尝变异测试

329 阅读2分钟

背景

代码覆盖率是衡量代码质量的最常见指标,但不能保证测试案例正在测试预期的行为,不能说明测试案例的准确性和完整性.一个很常见的反例就是测试案例没有断言,只验证了被测代码没有抛出异常,不能说明被测代码符合预期.

变异测试

变异测试的思路是以一种简单的方式修改被测代码,检查该代码的测试案例是否会检测到并报错.
通过变异因子来修改被测代码,pitest常见的变异因子有取反算术运算符,空返回,边界条件

变异测试的结果:

  • KILLED:这表示该变异因子已经被杀死,测试案例已正确测试了被测代码.
  • SURVIVED:这表示变异因子幸存下来,意味着测试案例未能完整和准确的测试被测代码.
  • 无限循环/运行时错误:这通常意味着在这种情况下无法发生突变.

如果变异测试结果大部分都是KILLED,说明测试案例是设计良好的,能够测试出代码中的错误.反之,如果变异测试结果大部分都是SURVIVED,则测试案例堪忧,测试案例的通过并不意味着程序代码没有缺陷.

变异测试举例

举个例子,一个被测方法是校验年龄有效,测试案例只设计了1个,即输入年龄100,返回有效

/**
 * 校验年龄有效
 * @return 1代表有效, -1代表无效
 */
public int isValidAge(Integer age) {
    if (age > 0 && age <= 120) {
        return 1;
    }
    return -1;
}
@Test
public void testMyService() {
    Assertions.assertEquals(1, myService.isValidAge(100));
}

此时得到的变异报告如下,指出了三个SURVIVED的变异因子,其中第13行没有测试边界条件,包括大于0和小于等于120,同时第16行返回-1无效的也未被测试

image.png

根据变异测试报告优化测试案例

根据报告我们添加两个测试案例,分别测试了0和120两个边界值,也测试了返回-1无效的场景

@Test
public void testMyService() {
    Assertions.assertEquals(1, myService.isValidAge(100));
    Assertions.assertEquals(1, myService.isValidAge(120));
    Assertions.assertEquals(-1, myService.isValidAge(0));
}

此时,全部变异因子都是KILLED,意味着测试案例具备准确性和完备性.

image.png