Mutation Testing con Stryker.NET

Mutation Testing con Stryker.NET

Mutation Testing con Stryker.NET

Come testare la qualità dei tuoi test in ambiente .NET

Indice dei Contenuti Mutation Testing con Stryker.NET

  1. Che cos’è il Mutation Testing?
  2. Perché usare Stryker.NET?
  3. Installazione e configurazione: mini guida pratica
  4. Tipi di mutazioni supportate
  5. Interpretare i risultati: killed, survived, timeout
  6. Integrazione nel pipeline CI/CD
  7. Best practice, pro e contro
  8. Formazione continua: la chiave del successo
  9. Conclusioni e contatti

Se sei uno sviluppatore .NET, avrai già sentito parlare di code coverage: quella metrica che misura quante righe di codice vengono eseguite dai tuoi test. Ma attenzione: avere il 90% di copertura non significa necessariamente avere test di qualità. I tuoi test potrebbero passare anche se la logica applicativa fosse completamente sbagliata. È qui che entra in gioco il mutation testing — e in ambito .NET, lo strumento di riferimento è Stryker.NET.

1. Che cos’è il Mutation Testing?

Il mutation testing è una tecnica avanzata di verifica della qualità dei test unitari. Il concetto è semplice: invece di testare il codice applicativo, proviamo a “testare i test” introducendo appositamente dei piccoli bug nel codice sorgente — chiamati mutanti — e verificando che i test esistenti li rilevino.

In pratica, uno strumento di mutation testing modifica automaticamente il codice (ad esempio cambia un operatore > con >=, oppure un && con ||), poi riesegue la suite di test. Se i test falliscono, il mutante è stato “ucciso” (killed): significa che i test sono abbastanza robusti da rilevare quella variazione. Se i test continuano a passare nonostante il bug introdotto, il mutante è “sopravvissuto” (survived): è un campanello d’allarme che indica una lacuna nei test.

Questo approccio supera i limiti del tradizionale code coverage, che misura solo quante linee vengono eseguite, non se vengono effettivamente verificate con asserzioni significative.

2. Perché usare Stryker.NET?

Stryker.NET è lo strumento ufficiale di mutation testing per l’ecosistema .NET. Fa parte della famiglia Stryker Mutator, nata nel 2018 proprio per colmare un vuoto nel panorama .NET rispetto ad altri linguaggi come Java (che ha PIT) o JavaScript (che ha Stryker JS).

Secondo la documentazione ufficiale Microsoft, Stryker.NET esegue automaticamente le mutazioni nel codice, lancia i test e genera un report HTML dettagliato con i risultati. La versione più recente disponibile su NuGet (aprile 2026) è la 4.14.1.

Ecco alcune ragioni per cui dovresti considerarlo nel tuo progetto:

  • Identifica test deboli o incompleti che il solo code coverage non rivela
  • Forza il team a scrivere asserzioni precise e mirate
  • Produce report HTML visivi e interattivi, facili da leggere
  • Supporta l’integrazione con Azure Pipelines, GitHub Actions e altri strumenti CI/CD
  • Compatibile con .NET Core, .NET 5/6/7/8/9 e .NET Framework
  • Altamente configurabile tramite file JSON

3. Installazione e configurazione: mini guida pratica

3.1 Installazione globale

Il modo più rapido per iniziare è installare Stryker.NET come strumento globale .NET. Apri il terminale e lancia:

dotnet tool install -g dotnet-stryker

Per aggiornarlo in futuro, usa:

dotnet tool update -g dotnet-stryker

3.2 Installazione locale al progetto (consigliata in team)

Per garantire che tutti i membri del team usino la stessa versione, è preferibile l’installazione locale:

# Nella cartella del progetto di testdotnet new tool-manifestdotnet tool install dotnet-stryker

Il file dotnet-tools.json generato può essere versionato su Git, così ogni sviluppatore avrà automaticamente accesso alla versione corretta.

3.3 Prima esecuzione

Posizionati nella directory del progetto di test e avvia Stryker.NET:

dotnet stryker

Per la maggior parte dei progetti .NET Core non è necessaria alcuna configurazione aggiuntiva: Stryker trova autonomamente il progetto sorgente da mutare. Al termine, trovi il report HTML nella cartella StrykerOutput.

3.4 File di configurazione stryker-config.json

Per un controllo più preciso, crea il file stryker-config.json nella root del progetto di test. Esempio di configurazione avanzata:

{  "stryker-config": {    "threshold-high": 80,    "threshold-low": 60,    "break-at": 50,    "ignore-mutations": ["string"],    "ignore-methods": ["*Logs", "*ToString"],    "mutate": [      "!**/Migrations/**",      "!**/*.Designer.cs"    ]  }}

Le opzioni principali:

  • threshold-high / threshold-low: soglie per colorare il report (verde/giallo/rosso)
  • break-at: se il mutation score scende sotto questa soglia, Stryker esce con codice di errore (utile in CI/CD)
  • ignore-mutations: esclude certi tipi di mutazione rumorosi o non rilevanti
  • mutate: esclude file o cartelle dal processo (es. migrazioni EF Core, file autogenerati)

4. Tipi di mutazioni supportate

Stryker.NET supporta numerose categorie di mutazioni. Ecco le principali:

TipoDescrizioneEsempio
EquivalenceSostituisce un operatore con uno equivalentex < y diventa x <= y
ArithmeticSostituisce operatori aritmeticix + y diventa x – y
LogicalInverte operatori logicix && y diventa x || y
StringSvuota le stringhe letterali“testo” diventa “”
ConditionalInverte condizioni booleanetrue diventa false
Null coalescingModifica operatori di null-checka ?? b viene alterato

La lista completa delle mutazioni supportate è disponibile nella documentazione ufficiale: stryker-mutator.io/docs/stryker-net/mutations.

5. Interpretare i risultati: killed, survived, timeout

Dopo l’esecuzione, Stryker.NET classifica ogni mutante in tre categorie:

  • Killed (ucciso): il test ha rilevato il mutante e fallito. Ottimo risultato! Significa che i test sono sufficientemente precisi.
  • Survived (sopravvissuto): il test non ha rilevato il bug introdotto. Questo è un punto critico: bisogna aggiungere o migliorare i test in quell’area.
  • Timeout: la mutazione ha generato un loop infinito o un percorso di esecuzione molto lento. Richiede analisi della logica o aumento del timeout configurato.

Il Mutation Score è la percentuale di mutanti uccisi sul totale. Un valore dell’80% o superiore è generalmente considerato un buon punto di partenza per la maggior parte dei progetti aziendali. Non perseguire il 100% a tutti i costi: concentrati sulle aree critiche per il business.

Esempio pratico: considera questo metodo e il relativo test iniziale, tratto dalla documentazione Microsoft:

// Codice sorgentepublic decimal CalculatePrice(decimal price, decimal discountPercent){    if (price <= 0)        throw new ArgumentException("Price must be greater than zero.");
    if (discountPercent < 0 || discountPercent > 100)        throw new ArgumentException("Discount percent must be between 0 and 100.");
    var discount = price * (discountPercent / 100);    return Math.Round(price - discount, 2);}

Un test iniziale che verifica solo il caso “happy path” lascerà sopravvivere molti mutanti sulle condizioni di boundary (es. price == 0, discountPercent == 100). Stryker.NET li individuerà tutti, guidandoti verso test più robusti come:

[Fact]public void InvalidPrice_ShouldThrowExceptionWithCorrectMessage(){    var calculator = new PriceCalculator();    // Cambiato da -10 a 0 per coprire il boundary price <= 0    var ex = Assert.Throws<ArgumentException>(        () => calculator.CalculatePrice(0, 10));    Assert.Equal("Price must be greater than zero.", ex.Message);}

Con questo approccio iterativo, esegui Stryker, analizza i mutanti sopravvissuti, migliora i test, ripeti, la qualità della tua suite cresce in modo misurabile e continuo.

6. Integrazione nelle pipeline CI/CD

Stryker.NET si integra facilmente in Azure Pipelines o GitHub Actions. Grazie all’opzione break-at nel file di configurazione, il tool restituisce un exit code non zero se il mutation score scende sotto la soglia definita, bloccando automaticamente il merge o il deploy.

Esempio di step in un file GitHub Actions (.yml):

- name: Run Mutation Testing  run: |    cd tests/MyApp.Tests    dotnet stryker  # Il pipeline si blocca se mutation score < break-at (es. 50)

Un suggerimento pratico: poiché il mutation testing è computazionalmente pesante (esegue la suite di test centinaia di volte), valuta di configurarlo come job parallelo o schedulato (es. notturno), separato dagli step più rapidi di unit test e linting. Su codebase grandi, puoi usare l’opzione mutate per limitare l’analisi ai soli file modificati nella PR.

7. Best practice, pro e contro

Best Practice

  • Inizia dai moduli critici: non applicare il mutation testing a tutto il progetto in una volta. Parti dalle classi con logica di business più delicata.
  • Usa il file stryker-config.json: versiona la configurazione in Git per garantire consistenza nel team.
  • Escludi il codice autogenerato: migrazioni EF Core, file .Designer.cs e simili vanno esclusi per non inquinare i risultati.
  • Non puntare al 100%: un mutation score tra 70-85% è generalmente ottimale per progetti enterprise. Sopra l’85% il ritorno diminuisce.
  • Integra progressivamente nel CI/CD: inizia con soglie basse (es. break-at: 40) e alzale nel tempo man mano che il team migliora la suite.
  • Affianca al code coverage, non sostituirlo: i due strumenti si completano a vicenda.
Pro Contro
Rileva test superficiali che il coverage non vedeEsecuzione lenta su codebase grandi
Forza asserzioni precise e significativePuò generare falsi allarmi (mutanti equivalenti)
Report HTML interattivi e dettagliatiCurva di apprendimento iniziale per il team
Integrazione nativa con Azure Pipelines e GitHub ActionsNon adatto a test di integrazione o E2E
Compatibile con xUnit, NUnit, MSTestRichiede disciplina nella configurazione e manutenzione
Open source, gratuito, attivamente mantenutoPuò rallentare il CI se non ottimizzato correttamente

8. Formazione continua: la chiave del successo

Il mercato del software è in continua e rapida evoluzione. Nuovi framework, nuove pratiche DevOps, nuovi standard di qualità emergono ogni anno. In questo contesto, l’unico modo per mantenere il proprio team competitivo e aggiornato è investire in formazione continua.

Conoscere strumenti come Stryker.NET non basta: per sfruttarli al meglio è necessario comprenderli a fondo nel contesto dell’intero ciclo di sviluppo .NET, dalla scrittura di test unitari efficaci, alla gestione della CI/CD pipeline, fino all’integrazione con Azure DevOps.

Per questo InnovaFormazione offre percorsi di formazione professionale dedicati a sviluppatori e team IT:

Grazie al Fondo Interprofessionale Fondimpresa, le aziende possono accedere a formazione finanziata per i propri dipendenti, abbattendo significativamente i costi. InnovaFormazione supporta le imprese nella gestione di tutto l’iter burocratico per l’accesso ai fondi.

9. Conclusioni

Il mutation testing con Stryker.NET rappresenta un salto di qualità nella verifica della robustezza dei test unitari in ambiente .NET. Non si tratta di sostituire le pratiche esistenti, come il code coverage o i test di integrazione, ma di affiancarle con uno strumento che risponde alla domanda fondamentale: “I miei test rileverebbero davvero un bug?”

In un mercato in cui la qualità del software è un differenziale competitivo, adottare strumenti come Stryker.NET è una scelta strategica. Ma l’adozione di nuovi strumenti richiede competenze aggiornate e una cultura della qualità diffusa in tutto il team. La formazione continua non è un costo: è un investimento sul capitale umano e sulla competitività dell’azienda.

Vuoi portare il tuo team al livello successivo? Scopri i nostri corsi dedicati. Visiona il catalogo completo QUI.

Per informazioni sui corsi, richiesta di preventivi e accesso ai finanziamenti Fondimpresa, contattaci:

Email: info@innovaformazione.net

Tel: 347 101 2275 — Dario Carrassi

Sito: innovaformazione.net

Per altri articoli tecnici di settore consigliamo di navigare sul nostro blog QUI.

(fonte) (fonte) (fonte)

Ti potrebbe interessare

Articoli correlati