Introduction Mutation Testing

De Reservoircode
Aller à : navigation, rechercher

Origine du test par mutation

Résultat du travail mené par Richard Lipton à l'issu de son Phd en 1971 à Yales. C'est une technique de test, qui a mis du temps à se généraliser, car très gourmande à l'usage. En effet, la génération des mutants demande pas mal de temps et de ressources sur les projets standards.

Un aperçu rapide

Mise en oeuvre

En principe, le test par mutation agit de la sorte :

On détermine la couverture de tests nominale en indiquant quelles seront les classes ou packages à tester. Puis, on détermine du jeu de mutations à mettre en place. En effet, il existe plusieurs type de mutations possibles, comme par exemple :

&& pour ||
== pour !=
> pour >=
+ pour -
...

On lance le processus de mutation de code, On valide que nos tests échoues (ou pas) après mutation, On génère un rapport de tests ou l'on faut échouer un job automatiser de tests.

Exemple de mutation de simple

Voici un exemple simple de mutation de code dans un test.

if (a == b) {
  // do something
}

if (a != b) {
  // do something
}

Les outils à disposition

Pour Java, il existe quelques projets qui sortent du lot et qui sont à la fois riches en features et documentation. On retrouve par exemple :

  • PIT
  • uJava
  • Mutator
  • Jumble
  • ...

Le plus actif et moderne semble être PIT et c'est celui-ci que j'ai décidé d'utiliser.

Test et intégration de PIT

Paramétrage de POM Maven

Pour démarrer un test rapide sur une base de code restreinte, on peut paramétrer PIT dans la POM de la sorte.

<plugins>
    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>build-helper-maven-plugin</artifactId>
    </plugin>
    <plugin>
        <groupId>org.pitest</groupId>
        <artifactId>pitest-maven</artifactId>
        <version>1.4.0</version>
        <configuration>
            <targetClasses>
                <param>com.reservoircode.services.*</param>
            </targetClasses>
        </configuration>
    </plugin>
</plugins>

Lancement de la commande Maven

Cette commande va générer un rapport disponible dans target/pit-reports/YYYYmmDD

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

Exemple de rapport

14:06:09 PIT >> INFO : MINION : 14:06:09 PIT >> INFO : Checking environment
14:06:09 PIT >> INFO : MINION : 14:06:09 PIT >> INFO : Found  14 tests
14:06:09 PIT >> INFO : MINION : 14:06:09 PIT >> INFO : Dependency analysis reduced number of potential tests by 0
14:06:09 PIT >> INFO : MINION : 14:06:09 PIT >> INFO : 14 tests received
14:06:11 PIT >> INFO : MINION : 14:06:11 PIT >> WARNING : More threads at end of test (11) should_test_all_user_login_scenarii(com.reservoircode.services.MyServiceIT) than start. (4)
-14:06:11 PIT >> INFO : Calculated coverage in 2 seconds.
14:06:11 PIT >> INFO : Created  10 mutation test units
-14:06:23 PIT >> INFO : Completed in 15 seconds
================================================================================
- Timings
================================================================================
> scan classpath : < 1 second
> coverage and dependency analysis : 2 seconds
> build mutation tests : < 1 second
> run mutation analysis : 12 seconds
--------------------------------------------------------------------------------
> Total  : 14 seconds
--------------------------------------------------------------------------------
================================================================================
- Statistics
================================================================================
>> Generated 139 mutations Killed 44 (32%)
>> Ran 56 tests (0.4 tests per mutation)
================================================================================
- Mutators
================================================================================
> org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator
>> Generated 2 Killed 1 (50%)
> KILLED 1 SURVIVED 1 TIMED_OUT 0 NON_VIABLE 0 
> MEMORY_ERROR 0 NOT_STARTED 0 STARTED 0 RUN_ERROR 0 
> NO_COVERAGE 0 
--------------------------------------------------------------------------------
> org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator
>> Generated 7 Killed 2 (29%)
> KILLED 2 SURVIVED 0 TIMED_OUT 0 NON_VIABLE 0 
> MEMORY_ERROR 0 NOT_STARTED 0 STARTED 0 RUN_ERROR 0 
> NO_COVERAGE 5 
--------------------------------------------------------------------------------
> org.pitest.mutationtest.engine.gregor.mutators.ReturnValsMutator
>> Generated 77 Killed 21 (27%)
> KILLED 21 SURVIVED 2 TIMED_OUT 0 NON_VIABLE 0 
> MEMORY_ERROR 0 NOT_STARTED 0 STARTED 0 RUN_ERROR 0 
> NO_COVERAGE 54 
--------------------------------------------------------------------------------
> org.pitest.mutationtest.engine.gregor.mutators.MathMutator
>> Generated 6 Killed 0 (0%)
> KILLED 0 SURVIVED 0 TIMED_OUT 0 NON_VIABLE 0 
> MEMORY_ERROR 0 NOT_STARTED 0 STARTED 0 RUN_ERROR 0 
> NO_COVERAGE 6 
--------------------------------------------------------------------------------
> org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
>> Generated 47 Killed 20 (43%)
> KILLED 20 SURVIVED 1 TIMED_OUT 0 NON_VIABLE 0 
> MEMORY_ERROR 0 NOT_STARTED 0 STARTED 0 RUN_ERROR 0 
> NO_COVERAGE 26 
--------------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 38.716 s
[INFO] Finished at: 2018-06-06T14:06:23+02:00
[INFO] ------------------------------------------------------------------------

Conclusion

Cette (très) brève introduction avait pour but de prendre en main un outil améliorant encore la qualité de notre logiciel, mais comme d'autres outils à la périphérie d'une CI/CD, PIT aura tout son sens, si ce dernier s'inscrit dans votre chaîne de développement logiciel.