Mutation Testing en Kotlin : comment écrire du code qui n'a pas peur du changement

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Pourquoi le code coverage est un piège et le mutation testing une bouée de sauvetage

\n\n

Imaginez la situation : votre projet Kotlin est couvert par des tests à 95%. Le CI est vert, le manager est content, le code part en production. Et une semaine plus tard — un bug en production. Cela vous dit quelque chose ? Il y a de fortes chances que vos tests s'exécutent simplement, mais ne vérifient pas la logique. C'est ici qu'entre en scène le mutation testing (test de mutation) — une méthode qui empêche les tests de "tricher".

\n\n

Dans cet article, nous allons voir ce qu'est le mutation testing en Kotlin, comment il fonctionne et comment l'intégrer dans un projet à l'aide de l'outil populaire Pitest. Vous découvrirez pourquoi une couverture de code à 100% n'est pas une garantie de qualité et comment le test de mutation aide à écrire des tests vraiment fiables.

\n\n

Qu'est-ce que le mutation testing ? Je l'explique avec des chats

\n\n

Supposons que vous ayez écrit une fonction qui détermine si un chat peut passer par une porte :

\n\n
fun canCatFitThroughDoor(catWidth: Int, doorWidth: Int): Boolean {\n    return catWidth <= doorWidth\n}
\n\n

Vous avez écrit un test :

\n\n
@Test\nfun `un chat de 30 passe par une porte de 40`() {\n    assertTrue(canCatFitThroughDoor(30, 40))\n}
\n\n

La couverture est de 100%. Mais que se passe-t-il si quelqu'un change accidentellement <= en < ? Le test passera quand même ! Le mutation testing introduit automatiquement ces "petites pannes" (mutations) dans le code et vérifie si au moins un test échoue. Si le test ne tombe pas, vos tests ne détectent pas cette erreur. C'est ce qu'on appelle le mutation testing en Kotlin.

\n\n

Comment fonctionne le mutation testing : des opérateurs aux mutants "survivants"

\n\n

L'outil (par exemple, Pitest pour Kotlin) crée des mutants — des versions de votre code avec un seul changement. Voici les mutations typiques :

\n\n
    \n
  • Modification des opérateurs de comparaison : > → >=, == → !=
  • \n
  • Inversion des conditions : if (a > b) → if (a <= b)
  • \n
  • Suppression d'appels de méthodes : someMethod() → vide
  • \n
  • Substitution de valeurs de retour : true → false
  • \n
  • Modification de nombres : 10 → 11 ou 0
  • \n
\n\n

Après la création des mutants, tous vos tests sont exécutés. Les résultats se divisent en trois catégories :

\n\n
    \n
  • Mutants tués (Killed) — les tests ont échoué, la mutation a été détectée. Parfait !
  • \n
  • Mutants survivants (Survived) — les tests ont réussi, bien que le code ait été modifié. Un signal d'alarme.
  • \n
  • Couverts mais non tués (Covered but not killed) — les tests ont été exécutés mais n'ont pas remarqué le changement.
  • \n
\n\n

Votre objectif est d'avoir le moins possible de mutants survivants (idéalement 0). C'est ce qu'est le mutation testing du code Kotlin en action.

\n\n

Pitest : l'outil principal pour Kotlin

\n\n

Pour les langages JVM (Java, Kotlin), la norme de facto est Pitest (PIT Mutation Testing). Il prend en charge Kotlin via le plugin pitest-kotlin. Voici comment l'ajouter dans Gradle :

\n\n
plugins {\n    id("info.solidsoft.pitest") version "1.15.0"\n}\n\npitest {\n    targetClasses.set(listOf("com.myapp.*")) // quelles classes muter\n    targetTests.set(listOf("com.myapp.*"))    // quels tests exécuter\n    threads.set(4)                            // threads parallèles\n    outputFormats.set(listOf("XML", "HTML"))  // rapports\n    mutationThreshold.set(80)                 // pourcentage minimum de tués\n    timeoutConstInMillis.set(10000)           // timeout par test\n}\n\ndependencies {\n    testImplementation("org.pitest:pitest-kotlin:1.15.0")\n}
\n\n

Lancement avec une seule commande :

\n\n
./gradlew pitest
\n\n

Après exécution, dans build/reports/pites

Blogs

Book Recommendations