一、Mutation Testing(以下简称 MT)是什么?
艰深的讲就是 UT 的 UT
MT 批改局部源代码后,在扭转后的源代码上运行 UT,再比拟批改前后的差异,检测 UT 是否笼罩了各种场景。
本人了解就是针对某些误写谬误的,比方 >= 误写成了 <=,UT 可能笼罩不到,但 MT 能够笼罩到。
最重要的它可能进步 UT 的准确度,能够检测出即便 UT 笼罩 100%,然而某些逻辑判断依然无奈笼罩到的状况,更无效的升高了 bug 呈现的概率。
二、MT 的原理
1. 判断
大于变大于等于,具体如下:
原始逻辑 | MT 批改后逻辑 |
---|---|
< | <= |
<= | < |
> | >= |
>= | > |
// 源代码
if(a<b) {// 编码}
// 被 MT 批改为
if(a<=b){// 编码}
2. 计算
原始逻辑 | MT 批改后逻辑 |
---|---|
+ | – |
– | + |
* | / |
/ | * |
% | * |
<< | >> |
>> | << |
>>> | << |
// 源代码
int a = b + c;
// 被 MT 批改为
int a = b - c;
3.void 办法
MT 会间接正文掉某些 void 办法使之不运行
// 源代码
public void someVoidMethod(int i){// 编码}
public int foo() {
int i = 5;
someVoidMethod(i);
return i;
}
// 被 MT 批改为
public void someVoidMethod(int i){// 编码}
public int foo(){
int i =5;
return i;
}
4. 更多规定
更多规定可见,https://pitest.org/quickstart…
5. 具体 MT 操作
MT 的每一次代码批改都会生成一个 Mutant,每一个 UT 会针对每一个 Mutant 运行,将运行后果与原始的代码的 UT 后果比拟,如果批改后的 UT 后果和批改前的统一,就表明代码笼罩不够全面,会记录为 Survive,如果后果不统一,记录为 Killed,示意 UT 笼罩的比拟全。相似于下两图的操作:
三、MT 的配置
我的项目 pom.xml 减少以下配置,threads 能够减少多线程运行 MT,以升高运行工夫;timestampedReports 默认为 true,每次运行 MT 都会生成一个工夫戳的文件夹,批改为 false 示意间接在设置的根目录生成报告,不再创立工夫戳文件夹
<plugin>
<groupId>org.pitest</groupId>
<artifactId>pitest-maven</artifactId>
<version>1.6.3</version>
<configuration>
<threads>10</threads>
<excludedTestClasses>
<param>**.*IT</param>
</excludedTestClasses>
<outputFormats>
<outputFormat>xml</outputFormat>
<outputFormat>html</outputFormat>
</outputFormats>
<historyOutputFile>src/test/resources/mutationHistory</historyOutputFile>
<historyInputFile>src/test/resources/mutationHistory</historyInputFile>
<timestampedReports>false</timestampedReports>
</configuration>
</plugin>
如果只批改了局部代码,须要独自运行 MT,在以上配置中的 configuration 中减少以下内容,能够独自指定运行某个 class 的 MT
<targetClasses>
<param>com.a.b.c.AServiceImpl</param>
</targetClasses>
<targetTests>
<param>com.a.b.c.AServiceImplTest</param>
</targetTests>
如果须要批改更加严格的 MT 测试,能够在 configuration 中减少以下内容,其中能够批改的油 DEFAULTS,STRONGER,ALL,具体规定见 https://pitest.org/quickstart…
<mutators>
<mutator>DEFAULTS</mutator>
</mutators>
执行命令进行 MT
mvn org.pitest:pitest-maven:mutationCoverage
本地运行 MT,如果代码 UT 以及源代码较多,会耗时很久,因而须要通过下面的配置 historyOutputFIle,每次执行结束会生成历史文件,下次再次运行 MT,只会运行有源代码变动的 UT。缩短前期每次的执行工夫。具体如下截图,内容是
四、运行实现的报告
绿色代表曾经通过 MT 的笼罩,红色示意没有被笼罩
五、我在针对 MT 测试的状况下如何写 UT
笼罩要全,各个分支都要笼罩到,其实失常咱们本人写 UT 时候也应该默认笼罩到(KILLED),如果没有笼罩到会显示(SURVIVED)
// 源代码
public String testMT (String code) {if(code.equals("1")){return "SUCCESS";}
return "FAIL";
}
//UT
@Test
public void testMT(){Assert.assertEquals("SUCCESS",mtDemo.testMT("1"));
Assert.assertEquals("FAIL",mtDemo.testMT("2"));
}
如果某些逻辑状况下是 throw 出 XXException 时候,须要在 test 注解中减少以下内容,也可能笼罩 MT@Test(expected=XXException.class)
源代码中尽量返回有意义的后果,防止应用 void,返回 void 时,MT 强行正文掉的状况,无论如何都无奈通过断言去判断(其实感觉这种状况就是模仿在写代码的时候,实现了编写 void 的办法后,然而忘了在主办法中调用了的状况)。
// 明确的返回能够断言判断
Assert.assertEquals("SUCCESS",mtDemo.testMT("1"));
if 判断中有多个条件的,mock 返回的须要笼罩到最初一个判断,上面图中显示领取和 param2 为 1,然而 param1 不合乎的时候就过了,MT 就认为笼罩不够全 main,然而 UT 批改后减少另外一种状况 param1 合乎,param2 不合乎,正好完满笼罩,MT 则示意 Kill,提醒笼罩齐全
// 源代码
public String testIf(String param1,String param2){if("1".equals(param1) || "1".equals(param2)){return "SUCCESS";}else{return "FAIL";}
}
//UT
@Test
public void testIf(){Assert.assertEquals("SUCCESS",mtDemo.testIf("0","1"));
}
@Test
public void testIf(){Assert.assertEquals("SUCCESS",mtDemo.testIf("0","1"));
Assert.assertEquals("SUCCESS",mtDemo.testIf("1","0"));
}
还有一些简单的状况,例如 lambda,实际上也是要笼罩到不同的状况返回不同的后果
// 源代码
static class TestDto{
private String name;
private String type;
TestDto(String name,String type){
this.name = name;
this.type = type;
}
public String getName(){return this.name;}
}
public List<TestDto> testLambda (List<TestDto> list) {return list.stream()
.filter(l-> l.getName().equals("name1"))
.collect(Collectors.toList());
}
//UT,只笼罩了 name1 存在的状况,如果 name1 不存在状况没笼罩
@Test
public void testLambda(){List<MTDemo.TestDto> list = new ArrayList<>();
MTDemo.TestDto dto1 = new MTDemo.TestDto("name1","type1");
MTDemo.TestDto dto2 = new MTDemo.TestDto("name2","type2");
list.add(dto1);
list.add(dto2);
Assert.assertEquals("name1",mtDemo.testLambda(list).get(0).getName());
}
这时 MT 会强制批改 lambda 中的 fiter 局部,间接改为 treu/false 生成两个 Mutant,会有一种没有笼罩到,MT 会标注 SURVIVED
// 再减少一个 UT,笼罩到 name1 不存在的状况
@Test
public void testLambda2(){List<MTDemo.TestDto> list = new ArrayList<>();
MTDemo.TestDto dto2 = new MTDemo.TestDto("name2","type2");
list.add(dto2);
Assert.assertEquals(0,mtDemo.testLambda(list).size());
}