Tests de mutation avec Infection
Mesurez la qualité réelle de vos tests.
Tests de mutation avec Infection est une leçon PHP Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage PHP Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours PHP Academy comprend 4 leçons au total.
La couverture peut tromper
Une couverture de 100 % des lignes semble rassurante, mais elle prouve seulement que vos essais ont exécuté le code — pas qu'ils détecteraient un bogue. Un essai peut exécuter une ligne sans rien vérifier de significatif. La mise à l'épreuve par mutation mesure la réalité : vos essais échoueraient-ils si le code était subtilement défectueux ? Infection est l'outil PHP standard pour cela.
composer require --dev infection/infectionIdée centrale : les mutants
Infection prend votre code couvert et y introduit de petites fautes — des mutants. Il transforme > en >=, + en -, && en ||, supprime un return, etc. Il réexécute ensuite vos essais sur chaque mutant :
- Si un essai échoue → le mutant est éliminé (bien — vos essais ont détecté la faute).
- Si tous les essais réussissent → le mutant a survécu (mal — un vrai bogue ici passerait inaperçu).
Un survivant en action
Considérez cette fonction et un essai faible. Infection transformerait >= en >. Si aucun essai ne vérifie la frontière exacte (le montant est égal au seuil), ce mutant survit — révélant un cas limite non vérifié.
<?php
function qualifiesForFreeShipping(float $total): bool
{
return $total >= 50.0; // Infection mutates >= to >
}
// Weak test only checks 100 and 10 -> never tests exactly 50.0
var_dump(qualifiesForFreeShipping(100.0)); // true
var_dump(qualifiesForFreeShipping(10.0)); // false
var_dump(qualifiesForFreeShipping(50.0)); // true <-- the boundary the mutant exposes
Éliminer le mutant
Ajoutez la vérification de la frontière et le mutant >=-vers-> est éliminé : dans le code muté, 50.0 > 50.0 vaut false, donc l'essai échoue — exactement ce que nous voulons. La mise à l'épreuve par mutation vous indique littéralement quelles vérifications manquent.
<?php
use PHPUnit\Framework\TestCase;
final class ShippingTest extends TestCase
{
public function test_threshold_is_inclusive(): void
{
// Kills the >= -> > mutant
self::assertTrue(qualifiesForFreeShipping(50.0));
}
}
Configuration : infection.json
Le fonctionnement d'Infection est piloté par infection.json5. Vous déclarez les répertoires à muter, l'emplacement des journaux et les seuils minimaux de score pour que l'intégration continue réussisse. Faites pointer source.directories uniquement vers votre code de production — jamais vers vos essais.
{
"source": {
"directories": ["src"]
},
"logs": {
"text": "build/infection.log",
"html": "build/infection.html"
},
"mutators": {
"@default": true
},
"minMsi": 80,
"minCoveredMsi": 90
}Les métriques MSI
Infection fournit l'indicateur du score de mutation :
- MSI = mutants éliminés / nombre total de mutants. Il est pénalisé par le code non couvert (ces mutants ne peuvent pas être éliminés).
- MSI couvert = mutants éliminés / mutants présents sur des lignes couvertes. Il mesure la qualité de vos vérifications là où vous effectuez réellement des essais.
- Couverture du code par mutation = part du code qu'Infection a pu muter.
Une couverture élevée des lignes mais un MSI couvert faible est le signal classique d'essais pauvres en vérifications.
Exécuter Infection efficacement
La mise à l'épreuve par mutation coûte cher — la suite est réexécutée une fois par mutant. Deux accélérations majeures : exécutez les essais en parallèle avec --threads, et ne soumettez à mutation que le code touché par votre branche actuelle grâce au filtrage des différences Git, ce qui est idéal pour l'intégration continue des demandes de fusion.
vendor/bin/infection --threads=max --git-diff-lines --git-diff-base=origin/mainLire les survivants
La valeur réside dans la différence qu'Infection affiche pour chaque mutant survivant. Elle montre la ligne exacte et la modification que vos essais n'ont pas réussi à détecter. Considérez les survivants comme une liste de tâches : ajoutez soit la vérification manquante, soit reconnaissez que le mutant est inoffensif (un mutant équivalent).
- return $total >= 50.0;
+ return $total > 50.0;
# Mutant survived: no test asserts the inclusive boundary (total === 50.0)Mutants équivalents et exclusion
Certains mutants sont équivalents — ils modifient le code mais pas son comportement observable, de sorte qu'aucun essai ne pourrait jamais les éliminer. Ils ne peuvent pas être éliminés et font baisser le MSI injustement. Désactivez les mutateurs connus comme faux positifs sur du code précis plutôt que de fausser vos essais pour les traquer.
{
"mutators": {
"@default": true,
"Plus": {
"ignore": ["App\\Math\\Statistics::variance"]
}
}
}Là où la mise à l'épreuve par mutation est la plus utile
Comme elle est lente, ciblez-la judicieusement :
- Appliquez-la à la logique métier centrale — tarification, autorisations, calculs — où les bogues silencieux coûtent cher.
- Faites dépendre les demandes de fusion du MSI couvert des lignes modifiées, et non de celui de tout le dépôt.
- Ne cherchez pas 100 % globalement ; les rendements décroissants et les mutants équivalents rendent cette démarche inutilement coûteuse.
- Utilisez-la pour trouver les vérifications faibles, puis corrigez les essais — le score est un moyen, pas un objectif.
Des données de couverture sont nécessaires à sa rapidité
Infection ne soumet à mutation que les lignes effectivement couvertes par vos essais, il réutilise donc la couverture du lanceur d'essais. Avec Xdebug, c'est lent ; pcov est nettement plus rapide pour la couverture des lignes et constitue le moteur recommandé pour les exécutions de mutation. Infection peut aussi générer lui-même la couverture ou exploiter un rapport de couverture déjà produit dans l'intégration continue.
# Faster mutation runs: use pcov instead of Xdebug for coverage
php -d pcov.enabled=1 vendor/bin/infection --threads=max
# Or reuse coverage already generated by your PHPUnit step:
vendor/bin/infection --coverage=build/coverage --skip-initial-testsVérification rapide
Que vous indique un mutant survivant ?
Récapitulatif
Vous avez appris à mesurer la qualité des essais, pas seulement leur quantité :
- La mise à l'épreuve par mutation injecte de petites fautes (mutants) ; éliminé = faute détectée, survivant = lacune.
- La couverture montre l'exécution ; le MSI couvert montre la solidité des vérifications.
- Configurez le fonctionnement avec
infection.json5et les seuils MSI ; exécutez avec--threadset le filtrage des différences Git pour gagner du temps. - Les survivants constituent une liste de tâches ; surveillez les mutants équivalents et ignorez-les délibérément.
- Ciblez la logique centrale et faites dépendre les demandes de fusion du MSI des lignes modifiées plutôt que de rechercher une couverture globale de 100 %.
Questions Fréquemment Posées
La leçon « Tests de mutation avec Infection » est-elle gratuite ?
Oui — le texte complet de « Tests de mutation avec Infection » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours PHP Academy, passe à CoddyKit PRO. Le cours PHP Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Tests de mutation avec Infection » ?
Mesurez la qualité réelle de vos tests. Tu pratiques PHP Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer PHP Academy ?
Aucune expérience préalable n'est requise. PHP Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.
Combien de temps prend la leçon « Tests de mutation avec Infection » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon PHP Academy ?
Oui. Chaque leçon PHP Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Le flux de développement piloté par les tests
- Simulations et bouchons avec Mockery
- Tests d’intégration et tests fonctionnels
- Tests de mutation avec Infection