PHP Academy · Lektion

Mutationstestning med Infection

Mät hur bra dina tester verkligen är.

Lektion 4 av 413 steg

Mutationstestning med Infection är en gratis lektion i PHP Academy på CoddyKit. Detta är lektion 4 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för PHP Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i PHP Academy innehåller totalt 4 lektioner.

Täckningsgrad ljuger

100 procents radtäckning känns betryggande, men det visar bara att Era tester körde koden – inte att de skulle upptäcka ett fel i den. Ett test kan köra en rad utan att kontrollera något meningsfullt. Mutationstestning mäter det som verkligen spelar roll: skulle Era tester misslyckas om koden var subtilt trasig? Infection är standardverktyget för detta i PHP.

composer require --dev infection/infection

Grundidén: mutanter

Infection tar den kod som Era tester täcker och introducerar små fel – mutanter. Det ändrar > till >=, + till -, && till ||, tar bort en return och så vidare. Därefter körs Era tester mot varje mutant:

  • Om ett test misslyckas → mutanten är dödad (bra – Era tester upptäckte felet).
  • Om alla tester klarar sig → mutanten överlever (dåligt – ett verkligt fel här skulle förbli oupptäckt).

En överlevare i praktiken

Betrakta den här funktionen och ett svagt test. Infection skulle mutera >= till >. Om inget test kontrollerar den exakta gränsen (att beloppet är lika med tröskelvärdet) överlever mutanten – vilket avslöjar ett otestat gränsfall.

<?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

Döda mutanten

Lägg till påståendet för gränsfallet, så dör mutanten >=-till->: med den muterade koden är 50.0 > 50.0 false, så testet misslyckas – precis som vi vill. Mutationstestning visar bokstavligen vilka påståenden som saknas.

<?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));
    }
}

Konfiguration: infection.json

Infection styrs av infection.json5. Ni anger vilka kataloger som ska muteras, vart loggarna ska skrivas och vilka miniminivåer för resultatet som krävs för att CI ska godkännas. Ange endast produktionskoden i source.directories – aldrig Era tester.

{
  "source": {
    "directories": ["src"]
  },
  "logs": {
    "text": "build/infection.log",
    "html": "build/infection.html"
  },
  "mutators": {
    "@default": true
  },
  "minMsi": 80,
  "minCoveredMsi": 90
}

MSI-måtten

Infection rapporterar Mutation Score Indicator:

  • MSI = dödade / totalt antal mutanter. Påverkas negativt av kod utan täckning (dessa mutanter kan inte dödas).
  • Covered MSI = dödade / mutanter på täckta rader. Mäter hur bra Era påståenden är där Ni faktiskt testar.
  • Mutation Code Coverage = hur stor del av koden Infection över huvud taget kunde mutera.

Hög radtäckning men låg Covered MSI är det klassiska tecknet på tester med för få meningsfulla påståenden.

Kör Infection effektivt

Mutationstestning är kostsam – testsviten körs om en gång per mutant. Två stora hastighetsförbättringar är att köra tester parallellt med --threads och att endast mutera kod som berörs av Er aktuella gren genom filtrering av Git-diffar, vilket passar bra i CI för pull requests.

vendor/bin/infection --threads=max --git-diff-lines --git-diff-base=origin/main

Tolka överlevarna

Värdet finns i den diff som Infection skriver ut för varje överlevande mutant. Den visar exakt rad och ändring som Era tester inte lyckades upptäcka. Betrakta överlevarna som en att-göra-lista: lägg antingen till det saknade påståendet eller konstatera att mutanten är ofarlig (en ekvivalent mutant).

- return $total >= 50.0;
+ return $total > 50.0;

# Mutant survived: no test asserts the inclusive boundary (total === 50.0)

Ekvivalenta mutanter och ignorering

Vissa mutanter är ekvivalenta – de ändrar koden men inte dess observerbara beteende, så inget test skulle någonsin kunna döda dem. De kan inte dödas och drar därför ned MSI på ett orättvist sätt. Undertryck kända falska positiva mutatorer i specifik kod i stället för att manipulera testerna för att jaga dem.

{
  "mutators": {
    "@default": true,
    "Plus": {
      "ignore": ["App\\Math\\Statistics::variance"]
    }
  }
}

Var mutationstestning lönar sig

Eftersom mutationstestning är långsam bör Ni rikta den väl:

  • Använd den på kärnlogik i domänen – prissättning, behörigheter och beräkningar – där tysta fel blir kostsamma.
  • Ställ krav på Covered MSI för ändrade rader i pull requests, inte för hela kodbasen.
  • Jaga inte 100 procent globalt; avtagande nytta och ekvivalenta mutanter gör det slösaktigt.
  • Använd den för att hitta svaga påståenden och förbättra sedan testerna – resultatet är ett medel, inte målet.

Täckningsdata krävs för snabbhet

Infection muterar endast rader som Era tester faktiskt täcker, så det återanvänder testkörarens täckning. Med Xdebug är detta långsamt; pcov är dramatiskt snabbare för radtäckning och är den rekommenderade motorn för mutationstestkörningar. Infection kan också skapa täckningen själv eller använda en täckningsrapport som Ni redan har skapat i CI.

# 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-tests

Snabbkontroll

Vad visar en överlevande mutant?

Sammanfattning

Ni har lärt Er att mäta testernas kvalitet, inte bara mängd:

  • Mutationstestning injicerar små fel (mutanter); dödad = felet upptäcktes, överlevde = en lucka.
  • Täckningsgrad visar körning; Covered MSI visar påståendenas styrka.
  • Konfigurera via infection.json5 med MSI-trösklar; kör med --threads och Git-diff-filtrering för bättre hastighet.
  • Överlevare är en att-göra-lista; var uppmärksam på ekvivalenta mutanter och ignorera dem medvetet.
  • Rikta in Er på kärnlogik och ställ krav på MSI för ändrade rader i pull requests i stället för att jaga en global nivå på 100 procent.
Gratis att börja

Lär dig PHP med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
49
Lektioner
195

Vanliga frågor

Är lektionen ”Mutationstestning med Infection” gratis?

Ja – hela texten till ”Mutationstestning med Infection” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i PHP Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i PHP Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”Mutationstestning med Infection”?

Mät hur bra dina tester verkligen är. Ni övar på PHP Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig PHP Academy?

Du behöver inga förkunskaper. Utbildningen i PHP Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 4 av 4.

Hur lång tid tar lektionen ”Mutationstestning med Infection”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här PHP Academy-lektionen?

Ja. Varje PHP Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Arbetsflödet för testdriven utveckling
  2. Mockning och stubbning med Mockery
  3. Integrations- och funktionstestning
  4. Mutationstestning med Infection
← Tillbaka till PHP Academy