背景
代码覆盖率是衡量代码质量的最常见指标,但不能保证测试案例正在测试预期的行为,不能说明测试案例的准确性和完整性.一个很常见的反例就是测试案例没有断言,只验证了被测代码没有抛出异常,不能说明被测代码符合预期.
变异测试
变异测试的思路是以一种简单的方式修改被测代码,检查该代码的测试案例是否会检测到并报错.
通过变异因子来修改被测代码,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无效的也未被测试
根据变异测试报告优化测试案例
根据报告我们添加两个测试案例,分别测试了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,意味着测试案例具备准确性和完备性.