关于java:Mutation-Testing

一、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());
}

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理