多年以来,为源代码编写单元测试一直是一种好习惯。 并且还可以使用测试覆盖率报告来查看测试覆盖了多少代码。 尽管行+分支覆盖率报告非常有用,但是它并不能告诉您单元测试的实际效果。 因此,甚至在测试中没有一个断言的情况下,甚至有可能达到100%的覆盖率。
对更好的测试方法感兴趣,我参加了今年的“ 欢乐编码 ”会议期间的“突变测试”研讨会。
变异测试是执行和分析单元测试的结果及覆盖范围的一种截然不同的方法。 无需衡量单元测试“访问了”多少代码,而是确定单元测试实际上“测试了”多少代码。
那么它实际上是如何工作的
变异测试背后的基本思想是对(字节)代码进行小改动(变异),然后执行测试以查看是否被单元测试检测到。
可能的突变是将“ >
”更改为“ >=
”,将“ ++
”替换为“ --
”,并删除“ void
”方法调用。
因此,每个突变都会创建代码的变更版本,称为“突变”。
在进行实际的变异测试之前,首先需要针对原始代码执行单元测试,以查看是否没有测试失败。
然后,将对每个“突变体”运行单元测试(这可能非常耗时),看是否:
- 我们的单元测试检测到该突变体:测试失败,因此“突变体”被视为“已杀死”。
- 在我们的单元测试中,突变体仍然未被注意到:测试没有“没有”失败(“突变体”被认为是“存活”)并且没有注意到突变; 这意味着单元测试实际上是“未”测试(未发现)“突变体”的。
变异测试的一个例子
那么,这种“变异测试”实际上是如何工作的呢?
请考虑以下方法:
public String foo(int i) {
if ( i >= 0 ) {
return "foo";
} else {
return "bar";
}
}
单元测试仅包含一种测试方法:
@Test
public void testFoo() {
testee.foo(0);
}
如果我们要创建代码的“突变体”,将“ >=
”更改为“ >
”,该怎么办?
我们希望我们的单元测试方法能够检测到这一点,对吧? 好吧,在这种情况下不是因为测试方法不包含单个断言。
我们将更改一个“ testFoo”方法以包含一个断言:
@Test
public void testFoo() {
String result = testee.foo(0);
assertEquals("foo", result);
}
现在,我们的单元测试方法将失败并检测(也称为“杀死”)“突变”代码。
除了将“ >=
”更改为“ >
”以外,还可以创建其他“突变体”:
- 可以将第一个
return
方法更改为返回null
(而不是"foo"
);
由于使用“ assertEquals”语句,此“突变体”被“ testFoo”方法“杀死”,但仍未被注意到原始的“ testFoo”方法(没有任何断言)。 - 可以将第二个
return
方法更改为返回null
(而不是"bar"
);
由于没有测试方法实际覆盖此执行路径,因此该“突变”将不会被注意到。
注意 :一些变异测试工具(例如Java的PIT)甚至不会费心为第二个return
语句创建“变异”,因为它不会被单元测试所覆盖(如传统的行覆盖率所检测)。
等效突变导致假阳性
与传统的行+分支覆盖相比,突变覆盖可能会导致假阳性。
它可能“错误地”报告(假阳性)单元测试未检测到“突变”为“未”。
例如,考虑以下Java代码:
public int someNonVoidMethod() { return 0; }
public void foo() {
int i = someNonVoidMethod();
// do more stuff with i
}
在变异测试期间(使用具有某些“非”默认配置的PIT变异测试),可能会创建以下“变异”:
public int someNonVoidMethod() { return 0; }
public void foo() {
int i = 0;
// do more stuff with i
}
“ mutant”中的“ int i = 0
”语句在功能上与“ someNonVoidMethod
”返回0
的原始代码“等效”。
由于单元测试不会(也不应)失败,因此无法检测到这种“等效突变”。
因此,它将被报告为未覆盖,而实际上它是一个假阳性。
根据文档 ,在使用PIT(一种用于Java的突变测试框架)时,“等效突变”应使用“默认”的一组突变体最小化。
例如,默认情况下会禁用导致“ int i = 0
”等效突变的PIT的“非无效方法调用突变器”。
结论
在参加了研讨会,一些额外的调查并与PIT合作之后,我非常热衷于在不久的将来在当前项目中使用“变异测试”(从新组件开始)。
与传统的覆盖率报告相适应,变异测试覆盖率实际上可以衡量您的测试质量,不能像传统的覆盖率报告那样被愚弄。
如果您也有兴趣:
- 请查看克里斯·里默(Chris Rimmer)的这个非常有趣的演讲 ,内容涉及突变测试的基本概念。
- 此外,还有一家名为TheLadders的公司使用PIT突变测试工具撰写了一篇有趣的文章 。
- Filip van Laenen还写了一篇有关超载杂志第108版中的“变异测试”的文章。
- 最后但并非最不重要的一点是PIT突变测试网站上的文档。
翻译自: https://www.javacodegeeks.com/2014/03/joy-of-coding-and-mutation-testing-in-java.html