Verbeter je unit tests met Stryker’s mutatietesten

Technologie

Verbeter je unit tests met Stryker’s mutatietesten

Heb je als acceptatiecriterium voor je project een minimaal percentage code coverage door unit tests? En vraag je je soms af hoe het staat met de algehele kwaliteit van die tests? Dan gaat dit blog je op weg helpen. Dit blog gaat over de effectiviteit van unit tests.

In softwareontwikkeling, en met name bij unit testing, wordt vaak de focus gelegd op de code coverage van tests. Echter, dit geeft weinig inzicht in de kwaliteit van die tests. Zo kunnen ze bijvoorbeeld cruciale asserts missen. Maar wie controleert systematisch dat de tests effectief nieuwe fouten opsporen? Voor .NET-ontwikkelaars is er een oplossing: maak kennis met Stryker Mutator, een geautomatiseerde testtool die gebruik maakt van mutatietesten.

Stryker Dashboard

Wat is Mutation testing?

Mutation testing is een methode om de kwaliteit van je unit tests te evalueren. Hierbij wordt een mutatie, ook wel een mutant genoemd, in de code aangebracht. Bijvoorbeeld, een plus-operator wordt vervangen door een min-operator, zie afbeelding:

Voor elke gegenereerde mutant worden alle tests uitgevoerd om te controleren of er een test faalt. Als een test faalt, betekent dit dat de aangebrachte wijziging wordt opgemerkt en dat je tests dit scenario goed afdekken. Slagen alle tests echter, dan weten we dat dit scenario niet is gedekt en dat er een test moet worden aangepast of toegevoegd om ook dit scenario te dekken.

Stryker Mutator is een mutation testing framework dat automatisch mutaties genereert, de tests uitvoert en bepaalt hoeveel mutanten zijn "gekilled" of overleefd hebben. Stryker kan de resultaten op verschillende manieren laten zien, waaronder op een dashboard waar je een mutatiescore voor al je tests kunt bekijken.

Mutanten

Een mutant is een aanpassing in je code, waarbij er getest wordt of deze aanpassing wordt afgevangen door je tests. Stryker kan veel verschillende mutaties uitvoeren, welke allemaal netjes zijn gedocumenteerd. Voorbeelden zijn mutaties op gelijkheid, logica, booleans, LINQ-statements, regular expressions. Je kunt de complete lijst hier bekijken. (link naar https://stryker-mutator.io/docs/stryker-net/mutations/)

Zoals laten zien in het eerder gegeven voorbeeld, muteert Stryker je code met "mutanten" en voert unit tests uit:

  • Faalt ten minste één unit test door een mutatie? Mutant ‘gekilled’  → goede test cases.
  • Slagen de tests nog steeds? Mutant overleeft → test cases ontbreken.

Dit resulteert in een totaalscore van 0 tot 100%.

Hoe te gebruiken

Het is heel eenvoudig om Stryker te gebruiken voor en een analyse te starten (met de standaardconfiguratie). Open de opdrachtprompt, ga vervolgens naar het pad van je project en voer de volgende commando's uit:

 

dotnet new tool-manifest

dotnet tool install dotnet-stryker

dotnet stryker

 

Het eerste commando maakt een tool manifest file aan als dat nog niet aanwezig is, het tweede installeert de Stryker.NET-tool daarin, en het derde start de analyse. Standaard wordt er een HTML-rapport gegenereerd met de resultaten (wanneer je  ‘-o’ toevoegt aan het laatste commando, wordt het gegenereerde rapport automatisch geopend).

Build pipelines

Om de analyses op te zetten in Azure DevOps is een PowerShell-taak nodig die de Stryker-tool installeert, een andere taak die de analyse start (één per project) en tenslotte een extensie die het mutatierapport publiceert naar een extra tab in de build-samenvatting.

Met behulp van configuratie settings, kun je het mogelijk maken om een minimumscore voor nieuwe code voor een PR te vereisen. Als deze score niet wordt gehaald, faalt de build.

Optimalisaties

Een analyse kan enkele minuten duren, afhankelijk van de grootte van je unit tests en je projecten.  Zeker naarmate je codebase groeit, zullen er meer mutaties worden gegenereerd, wat zal leiden tot een toename van het aantal testuitvoeringen. Om te voorkomen dat je builds te lang duren, kun je ervoor kiezen om alleen de mutaties te testen van de bestanden die zijn aangepast. Hiervoor kun je de ‘since’-optie gebruiken. Met de ‘since’-optie ontvang je een rapport dat alleen de uitgevoerde tests bevat. Wil je echter elke keer het volledige rapport zien, dan kun je de ‘baseline’-optie gebruiken. Deze optie maakt ook gebruik van de ‘since’-optie, maar voegt de resultaten toe van eerder uitgevoerde tests. Dit gebeurt door een ‘baseline’-file aan te maken, dat je op verschillende manieren kunt opslaan.

Daarnaast zijn er nog een aantal configuratie opties om de snelheid van je mutatie tests te verhogen:

  • Verlaag het ingestelde mutatieniveau, uiteraard ten koste van het aantal mutanten en dus de grondigheid van de analyse.
  • Stel uitsluitingen in van methoden, klassen en bepaalde typen mutanten.
  • Standaard gebruikt Stryker de helft van het aantal logische cores in de machine; je kunt dit verhogen via configuratie.
RL 03257

Conclusie

Hoewel mutatietesten als techniek vrij oud is, is het nog steeds relatief onbekend onder .NET-ontwikkelaars. Onterecht, want het biedt een goed inzicht in de effectiviteit van de aanwezige unit tests. Je wilt er immers onder andere zeker van zijn dat je code correct functioneert en aan de eisen voldoet. Dit vereist volledige unit tests die de nodige scenario's afdekken, oproepen verifiëren en resultaten controleren. Ook is het onmisbaar bij het refactoren van bestaande code, zoals bij het verbeteren van codekwaliteit en het verlagen van technical debt. Bovendien helpt het om eventuele fouten in een vroeg stadium te detecteren.

Stryker biedt een laagdrempelige manier om dit geautomatiseerd te doen voor deze techniek en kan bovendien netjes worden geïntegreerd in je CI/CD-pipelines. Zelfs als je het niet integreert in de pipeline en alleen op je ontwikkelmachine draait, heeft het een positief effect: het traint je toch in het schrijven van effectieve en volledige unit tests. En daardoor word je een betere ontwikkelaar!

 

*Bronvermelding: https://mstack.nl/blogs/20230730-stryker-mutation-testing/

Rlxgaransys 2 0057 Michiel

Meer weten? Neem contact op!

Michiel Leenaers

Business Development
0614688177
m.leenaers@garansys.nl