Mutation Testing — Cheat Sheet

Tag: mutation-testing, testing, qa, coverage, stryker, pitest

Livello: avanzato

TL;DR: Valuta la reale efficacia dei test introducendo difetti intenzionali nel codice. Se i test passano nonostante il difetto, la test suite non è affidabile.

Concetti chiave

Il tool altera operatori o logica (es. cambia + in -, > in <) creando un "mutante".

Mutante ucciso (killed): un test fallisce. È il risultato desiderato.

Mutante sopravvissuto (survived): i test passano ignorando il difetto. Indica asserzioni mancanti o deboli.

Dimostra l'effettiva solidità dei test, superando l'illusione della pura code coverage.

Mutante equivalente: una mutazione sintattica che non altera il comportamento logico reale.

Altamente esigente a livello di CPU: ogni mutante richiede una nuova esecuzione isolata dei test.

Strumenti principali nel mercato: Stryker (ecosistema JS/TS, C#, Scala) e PIT (Java).

Domande tipiche e risposte pronte

Q: Che differenza c'è tra code coverage e mutation testing?R: La coverage dice solo se una riga è stata eseguita. Il mutation testing verifica se i test falliscono correttamente se quella riga viene corrotta.

Q: Perché non bloccare la build al 100% di mutation score?R: Il costo computazionale è eccessivo e il rischio di mutanti equivalenti genera falsi positivi. Meglio puntare su run differenziali per Pull Request.

Q: Come tratti un mutante equivalente?R: Valuto se il codice originale può essere scritto in modo meno ambiguo. Altrimenti disabilito la mutazione specifica tramite configurazione.

Insidie e errori comuni

Confidare ciecamente nel 100% di code coverage quando si hanno test poveri di asserzioni.

Applicare la mutazione all'intera codebase anziché confinarla ai moduli core e alla logica di business.

Sprecare ore per uccidere mutanti puramente tecnici ignorando l'affidabilità generale.

Dimenticare i timeout di test: mutanti su condizioni di uscita dei loop generano facilmente cicli infiniti.

Snippet e comandi

# Esecuzione standard di Stryker in un progetto Node.js
npx stryker run

TypeScript

// 1. Codice originale
const isAdult = (age: number) => age >= 18;

// 2. Mutante generato dal framework (es. Conditionals Boundary)
const isAdult = (age: number) => age > 18;

// Se nessun test verifica l'età ESATTA di 18 anni, il mutante sopravvive.

Da ripassare

[ ] Setup dell'esecuzione incrementale (diff basato su Git) per evitare colli di bottiglia in CI.

[ ] Disabilitazione granulare dei mutanti tramite commenti ignorati nel sorgente.

Riferimenti

Stryker Mutator Documentation

PIT Mutation Testing Documentation

Jarvis Interviews
Jarvis