Maven - PITest突然変異のテストの例

Maven – PITest突然変異テストの例

image

この記事では、Maven PIT mutation testing pluginを使用してJavaプロジェクトのミューテーションテストカバレッジレポートを生成する方法を示します。

でテスト済み

  1. Maven 3.5.3

  2. JUnit 5.3.1

  3. PITest 1.4.3

Note
JaCoCoのようなラインカバレッジツールは、コードがテストされているかカバーされているかを通知するだけですが、このPITestミューテーションカバレッジはテストの有効性を通知しようとします。

1. クイック– Maven PITestプラグイン

1.1 To enable PIT mutation testing, put this pitest-maven in pom.xml

pom.xml

    
        org.pitest
        pitest-maven
        1.4.3

        
            
                pit-report
                
                test
                
                    mutationCoverage
                
            
        

        
        
        
            
                org.pitest
                pitest-junit5-plugin
                0.8
            
        
        
            
                com.example.examples.*
            
            
                com.example.examples.*
            
        
    

1.2 Run the PITest manually.

$ mvn clean org.pitest:pitest-maven:mutationCoverage

1.3 Above pom.xml file attached the ‘mutationCoverage’ goal to Maven test phase. これで、Mavenテストを実行すると、PITestテストが自動的にトリガーされます。

$ mvn clean test

1.4 Report will be generated at target/pit-reports/YYYYMMDDHHMI/*

2. 突然変異試験とは

2.1 The mutation testing is used to measure the effectiveness of the test.

ミューテーションテストでは、mutators(数学演算子の切り替え、戻り値の型の変更、呼び出しの削除など)を使用して、コードを別のミューテーションにミューテーション/変更し(ミューテーターに基づいて新しいコードを作成)、ユニットかどうかを確認します。新しいミューテーションのテストは失敗します(ミューテーションは強制終了されます)。

テストの有効性は、いくつの突然変異が殺されたかの尺度となり得る。

2.2 For example, this code :

    public boolean isPositive(int number) {

        boolean result = false;
        if (number >= 0) {
            result = true;
        }
        return result;

    }

デフォルトでは、PITestは異なるmutatorsを使用して、上記のコードを異なるミューテーションに変換します(新しいコード):

#1突然変異–条件付き境界の変更(ミューテーター)

    public boolean isPositive(int number) {

        boolean result = false;
        // mutator - changed conditional boundary
        if (number > 0) {
            result = true;
        }
        return result;

    }

#2突然変異–否定条件付き(ミューテーター)

    public boolean isPositive(int number) {

        boolean result = false;
        // mutator - negated conditional
        if (false) {
            result = true;
        }
        return result;

    }

#3突然変異–整数サイズの値の戻り値を(x == 0? 1:0)(ミューテーター)

    public boolean isPositive(int number) {

        boolean result = false;
        if (number > 0) {
            result = true;
        }

        // mutator - (x == 0 ? 1 : 0)
        return !result;

    }

2.3 A Good unit test should fail (kill) all the mutations #1,#2,#3.

    @Test
    public void testPositive() {

        CalculatorService obj = new CalculatorService();
        assertEquals(true, obj.isPositive(10));

    }

上記の単体テストは突然変異#2と#3を殺します(単体テストは失敗します)が、突然変異#1は生き残ります(単体テストに合格します)。

image

2.4 Review the mutation #1 again. このテスト(突然変異)を失敗(強制終了)するには、条件付き境界である数値ゼロをテストする必要があります。

    public boolean isPositive(int number) {

        boolean result = false;
        // mutator - changed conditional boundary
        if (number > 0) {
            result = true;
        }
        return result;

    }

数字のゼロをテストすることにより、単体テストを改善します。

    @Test
    public void testPositive() {

        CalculatorService obj = new CalculatorService();
        assertEquals(true, obj.isPositive(10));
        //kill mutation #1
        assertEquals(true, obj.isPositive(0));

    }

完了、100%の突然変異カバレッジ。

image

3. Maven + PITestの例

自己参照のみを目的とした別の完全なMaven + PITestの例。

3.1 A full pom.xml

pom.xml



    4.0.0
    com.example.examples
    maven-mutation-testing
    jar
    1.0-SNAPSHOT

    
        
        UTF-8
        1.8
        1.8
        5.3.1
        1.4.3
    

    

        
        
            org.junit.jupiter
            junit-jupiter-engine
            ${junit.version}
            test
        

    
    
        maven-mutation-testing
        
            
                org.apache.maven.plugins
                maven-surefire-plugin
                3.0.0-M1
            

            
                org.pitest
                pitest-maven
                ${pitest.version}

                
                    
                        pit-report
                        test
                        
                            mutationCoverage
                        
                    
                

                
                
                
                    
                        org.pitest
                        pitest-junit5-plugin
                        0.8
                    
                
                
                    
                        com.example.examples.*
                    
                    
                        com.example.examples.*
                    
                
            

        
    

3.2 Source Code

StockService.java

package com.example.examples;

public class StockService {

    private int qtyOnHand;

    public StockService(int qtyOnHand) {
        this.qtyOnHand = qtyOnHand;
    }

    private void isValidQty(int qty) {
        if (qty < 0) {
            throw new IllegalArgumentException("Quality should be positive!");
        }
    }

    public int add(int qty) {

        isValidQty(qty);
        qtyOnHand = qtyOnHand + qty;
        return qtyOnHand;

    }

    public int deduct(int qty) {

        isValidQty(qty);

        int newQty = qtyOnHand - qty;
        if (newQty < 0) {
            throw new IllegalArgumentException("Out of Stock!");
        } else {
            qtyOnHand = newQty;
        }
        return qtyOnHand;

    }

}

3.3 Below unit test will kill all the mutations that are generated by PItest.

TestStockService.java

package com.example.examples;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class TestStockService {

    @DisplayName("Test deduct stock")
    @Test
    public void testDeduct() {
        StockService obj = new StockService(100);
        assertEquals(90, obj.deduct(10));
        assertEquals(0, obj.deduct(90));
        assertEquals(0, obj.deduct(0));

        Assertions.assertThrows(IllegalArgumentException.class, () -> {
            obj.deduct(-1);
        });

        Assertions.assertThrows(IllegalArgumentException.class, () -> {
            obj.deduct(100);
        });

    }

    @DisplayName("Test add stock")
    @Test
    public void testAdd() {
        StockService obj = new StockService(100);
        assertEquals(110, obj.add(10));
        assertEquals(110, obj.add(0));

        Assertions.assertThrows(IllegalArgumentException.class, () -> {
            obj.add(-1);
        });
    }


}

3.4 Run it.

$ mvn clean org.pitest:pitest-maven:mutationCoverage

#or

$ mvn clean test

3.5 Review the report at target\pit-reports\${YYYYMMDDHHMI}\index.html

image

image

4. FAQs

4.1 Study the PITest Mutators, so that we understand how the mutation is generated.

4.2 This mutation testing is a time consuming task, always declares the classes that are needed for the mutation test.

pom.xml

    
        org.pitest
        pitest-maven
        ${pitest.version}

        
            
                com.example.examples.*Calculator*
                com.example.examples.*Stock*
            
            
                com.example.examples.*
            
        
    

ソースコードをダウンロード

$ git clone https://github.com/example/maven-examples.git
$ cd maven-mutation-testing

$ mvn clean org.pitest:pitest-maven:mutationCoverage
#or
$ mvn clean test

target / pit-reports / YYYYMMDDHHMI /index.htmlでレポートを表示