Knowledge-base
  • Home
  • Samman Technical Coaching
  • Software craftsmanship
    • Practices
      • Pair Programming
      • Code Review
      • Co-designs
      • Design sessions
      • Interview Domain Experts
      • Dev ethics
    • The Software Craftsman
    • Egoless Crafting
    • Technical debt workshop
    • Functional Programming made easy in C# with Language-ext
    • F# for OO programmers
    • Domain Modeling Made Functional
    • Testing
      • Clean Tests
      • Improve the design and testing of your micro-services through CDC Tests
        • CDC testing made simple with Pact
        • Pact broker : the missing piece of your Consumer-Driven Contract approach
      • Improve your test quality with Mutation testing
      • How to name our Unit Tests
      • How to write better assertions
    • Katas
      • TDD
        • Stack kata
        • Fizzbuzz
        • Outside-in TDD (London Style)
      • Improve your software quality with Property-Based Testing
        • A journey to Property-Based Testing
      • Clean Code
      • Clean Architecture
      • Write S.O.L.I.D code
      • Mocking
      • Gilded Rose (Approval Testing)
      • Mikado method
        • Mikado kata
      • Pure functions
      • Theatrical players refactoring Kata
        • Let's refactor (OOP style)
        • Let's refactor (FP style)
      • Functional Programming made easy in Java & C#
      • Refactoring journey
      • Refactoring du Bouchonnois
        • 1) Se faire une idée du code
        • 2) "Treat warnings as errors"
        • 3) Let's kill some mutants
        • 4) Améliorer la lisibilité des tests
        • 5) "Approve Everything"
        • 6) Définir des propriétés
        • 7) Tests d'architecture
        • 8) Use Cases
        • 9) Tell Don't Ask
        • 10) "Avoid Primitives" - Commands
        • 11) "Avoid Exceptions"
        • 12) "Event Sourcing"
    • Software Design X-Rays
      • Workshop
    • The Programmer's Brain
      • How to read code better
  • Software Architecture
    • Fundamentals of Software Architecture
    • Aligning Product & Software Design
    • DDD re-distilled
    • Test your architecture with Archunit
    • NoSQL
  • Agile coaching
    • How to run a Community of Practices (COP)
    • The developers — the forgotten of agility
      • The secrets to re-on-board the devs in agility
    • Coaching toolbox
      • Echelle
      • Learning expedition
    • How to improve Team Decision making ?
      • Decision Making Principles and Practices
    • Learning 3.0
    • Retrospectives
      • Back to the Future
      • Mission Impossible
      • Movie themes
      • Rétro dont vous êtes le héros
      • Sad/Mad/Glad
      • Speed boat
      • Star wars theme
      • Story cubes
    • Technical Agile Coaching with the Samman Method
    • Xanpan - a team centric agile method story
    • XTREM WATCH — Découvrez la puissance de la veille collective
    • Become a better speaker through peer feedback
    • Project-to-Product Principles
  • Leadership
    • Bref. J'ai pris une tarte dans la gueule (et ça fait extrêmement de bien)
    • Forward Summit 2020
    • Learn leadership from the Navy SEALs
    • Learn to lead and help your team(s) to be successful
    • Towards a learning organization and beyond
    • Leadership is language
  • Serious games
    • My serious games
    • Libérez vos entretiens d’embauche avec la gamification
    • How to create a game
    • How to debrief a game ?
    • Lego Serious Play (LSP)
      • LSP in your job interviews
  • Xtrem Reading
    • Cultivate Team Learning with Xtrem Reading
    • My Book Infographics
    • How to make book infographics
    • En route vers l’apprenance avec Xtrem Reading
    • Resources
      • Book notes
        • Agile People: A Radical Approach for HR & Managers
        • Agile testing : A Practical Guide for Testers and Agile Teams
        • Boite à outils de l'intelligence émotionnelle
        • Building a better business using Lego Serious Play method
        • Building evolutionary architectures
        • Code that fits in your head
        • Culture Agile
        • Culture is everything
        • Domain-Driven Design: The First 15 Years
        • Dynamic Reteaming - The Art and Wisdom of Changing Teams
        • How to avoid a Climate Disaster
        • La liberté du commandement
        • Réaliser ses rêves, ça s'apprend
        • Refactoring at Scale
        • Succeeding with OKRs in Agile
        • Team Topologies
        • The Good Life
        • Tu fais quoi dans la vie
        • Who Does What By How Much?
  • My Activity
    • Retour sur mon année 2020
Powered by GitBook
On this page
  • Différents mutants
  • String mutation
  • Statement mutation
  • Linq Mutation
  • Tuer les mutants
  • ChasseurInconnu
  • PartieDeChasseService
  • LinQ mutation
  • Reflect

Was this helpful?

  1. Software craftsmanship
  2. Katas
  3. Refactoring du Bouchonnois

3) Let's kill some mutants

Previous2) "Treat warnings as errors"Next4) Améliorer la lisibilité des tests

Last updated 1 year ago

Was this helpful?

Prendre quelques instants pour découvrir la page .

Durant cette étape:

  • Lancer

    • Analyser les mutants survivants

  • Tuer autant de mutants que possible (atteindre un score de mutation d'au moins 90%)

Différents mutants

Stryker parvient à muter des string dans le code de production et ce changement n'est détecté par aucun test.

C'est le cas pour 2 classes : ChasseurInconnu et PartieDeChasseService.

Celà fait-il du sens de vérifier ce genre de strings depuis nos tests?

Stryker parvient à supprimer certains blocs de code, tels que :

Ajout d'événements dans l'aggrégat PartieDeChasse

Sauvegarde via repository

Stryker parvient à changer certaines expressions LinQ.

Tuer les mutants

ChasseurInconnu

On ajoute l'assertion du message métier dans les tests en repartant des tests listés dans le rapport de stryker:

[Fact]
public void EchoueCarLeChasseurNestPasDansLaPartie()
{
    ...
    chasseurInconnuVeutTirer
        .Should()
        .Throw<ChasseurInconnu>()
        .WithMessage("Chasseur inconnu Chasseur inconnu");

    repository.SavedPartieDeChasse().Should().BeNull();
}

On peut alors relancer stryker, notre score de mutation passe de 79.03% à 79.84%... on a encore du boulot mais on avance.

PartieDeChasseService

On doit ajouter la vérification d'événements et de sauvegarde de la partie de chasse dans nos tests.

[Fact]
public void EchoueAvecUnChasseurNayantPlusDeBalles()
{
    ...
    var service = new PartieDeChasseService(repository, () => DateTime.Now);
    var tirerSansBalle = () => service.TirerSurUneGalinette(id, "Bernard");

    tirerSansBalle.Should().Throw<TasPlusDeBallesMonVieuxChasseALaMain>();
    repository.SavedPartieDeChasse()!
        .Events
        .Should()
        .HaveCount(1)
        .And
        .EndWith(new Event(new DateTime(),"Bernard veut tirer sur une galinette -> T'as plus de balles mon vieux, chasse à la main"));
}
[Fact]
public void EchoueAvecUnChasseurNayantPlusDeBalles()
{
    var now = new DateTime(2024, 6, 6, 14, 50, 45);

    var service = new PartieDeChasseService(repository, () => now);
    var tirerSansBalle = () => service.TirerSurUneGalinette(id, "Bernard");

    tirerSansBalle.Should().Throw<TasPlusDeBallesMonVieuxChasseALaMain>();
    repository
        .SavedPartieDeChasse()!
        .Events
        .Should()
        .HaveCount(1)
        .And
        .EndWith(new Event(now,
            "Bernard veut tirer sur une galinette -> T'as plus de balles mon vieux, chasse à la main"));
}

On continue à tuer les autres mutants "similaires".

[Fact]
public void EchoueSiLaPartieDeChasseEstTerminée()
{
    ...

    var now = new DateTime(2024, 6, 6, 14, 50, 45);
    var service = new PartieDeChasseService(repository, () => now);

    var tirerQuandTerminée = () => service.TirerSurUneGalinette(id, "Chasseur inconnu");

    tirerQuandTerminée.Should()
        .Throw<OnTirePasQuandLaPartieEstTerminée>();

    repository
        .SavedPartieDeChasse()!
        .Events
        .Should()
        .HaveCount(1)
        .And
        .EndWith(new Event(now, "Chasseur inconnu veut tirer -> On tire pas quand la partie est terminée"));
}
public class PartieDeChasseServiceTests
{
    private static readonly DateTime Now = new(2024, 6, 6, 14, 50, 45);
    private static readonly Func<DateTime> TimeProvider = () => Now;

    private static void AssertLastEvent(PartieDeChasse partieDeChasse,
        string expectedMessage)
        => partieDeChasse.Events.Should()
            .HaveCount(1)
            .And
            .EndWith(new Event(Now, expectedMessage));
    
    ...
    
    [Fact]
    public void EchoueAvecUnChasseurNayantPlusDeBalles()
    {
        ...
        var service = new PartieDeChasseService(repository, TimeProvider);
        var tirerSansBalle = () => service.TirerSurUneGalinette(id, "Bernard");

        tirerSansBalle.Should().Throw<TasPlusDeBallesMonVieuxChasseALaMain>();
        AssertLastEvent(repository.SavedPartieDeChasse()!,
            "Bernard veut tirer sur une galinette -> T'as plus de balles mon vieux, chasse à la main");
    }

Après être repassé sur tous les tests et amélioré les assertions nous avons un score de mutation de 96.72%.

LinQ mutation

Ces mutations sont un peu particulières dans notre cas :

// On vérifie que le chasseur existe
if (partieDeChasse.Chasseurs.Exists(c => c.Nom == chasseur))
{
    // L'utilisation de First est dès lors "safe"
    var chasseurQuiTire = partieDeChasse.Chasseurs.First(c => c.Nom == chasseur);
    ...
}

De plus, en intégrant le mutant dans le code de production celui-ci ne compile plus...

On va changer le code de production afin de faire en sorte que ce mutant ne puisse plus être généré :

if (partieDeChasse.Chasseurs.Exists(c => c.Nom == chasseur))
{
    var chasseurQuiTire = partieDeChasse.Chasseurs.Find(c => c.Nom == chasseur)!;
    ...
}

On répète la même stratégie pour les autres mutations jusqu'à atteindre 100% en score de mutation 👍.

Reflect

Pour créer de bons tests, il est important de toujours se concentrer sur l'écriture de bonnes assertions et encore mieux développer en utilisant T.D.D.

Nous avons un problème avec la gestion du temps ici...

Nous changeons la manière dont on gère le temps dans ce test. Le score de mutation monte alors à 82.26%.

On a de la duplication dans les assertions, on en profite alors pour les mutualiser.

Nouveau rapport SonarCloud disponible .

Lorsqu'on écrit des tests (a priori ou posteriori), il est important d'avoir en tête certains principes tels que les .

🔴
🟢
🔵
String mutation
Statement mutation
Linq Mutation
ici
Test Desiderata
Mutation Testing
stryker
Step 3 : Let's kill some mutants
String mutation 1
String mutation 2
Statement mutation 1
Statement mutation 2
LinQ mutation