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]publicvoidEchoueAvecUnChasseurNayantPlusDeBalles(){...var service =newPartieDeChasseService(repository, () =>DateTime.Now);var tirerSansBalle = () =>service.TirerSurUneGalinette(id,"Bernard");tirerSansBalle.Should().Throw<TasPlusDeBallesMonVieuxChasseALaMain>();repository.SavedPartieDeChasse()! .Events .Should() .HaveCount(1) .And .EndWith(newEvent(newDateTime(),"Bernard veut tirer sur une galinette -> T'as plus de balles mon vieux, chasse à la main"));}
🔴 Nous avons un problème avec la gestion du temps ici...
[Fact]publicvoidEchoueAvecUnChasseurNayantPlusDeBalles(){var now =newDateTime(2024,6,6,14,50,45);var service =newPartieDeChasseService(repository, () => now);var tirerSansBalle = () =>service.TirerSurUneGalinette(id,"Bernard");tirerSansBalle.Should().Throw<TasPlusDeBallesMonVieuxChasseALaMain>(); repository .SavedPartieDeChasse()! .Events .Should() .HaveCount(1) .And .EndWith(newEvent(now,"Bernard veut tirer sur une galinette -> T'as plus de balles mon vieux, chasse à la main"));}
🟢 Nous changeons la manière dont on gère le temps dans ce test. Le score de mutation monte alors à 82.26%.
On continue à tuer les autres mutants "similaires".
[Fact]publicvoid EchoueSiLaPartieDeChasseEstTerminée(){...var now =newDateTime(2024,6,6,14,50,45);var service =newPartieDeChasseService(repository, () => now); var tirerQuandTerminée = () =>service.TirerSurUneGalinette(id,"Chasseur inconnu"); tirerQuandTerminée.Should() .Throw<OnTirePasQuandLaPartieEstTerminée>(); repository .SavedPartieDeChasse()! .Events .Should() .HaveCount(1) .And .EndWith(newEvent(now,"Chasseur inconnu veut tirer -> On tire pas quand la partie est terminée"));}
🔵 On a de la duplication dans les assertions, on en profite alors pour les mutualiser.
publicclassPartieDeChasseServiceTests{privatestaticreadonlyDateTime Now =new(2024,6,6,14,50,45);privatestaticreadonlyFunc<DateTime> TimeProvider = () => Now;privatestaticvoidAssertLastEvent(PartieDeChasse partieDeChasse,string expectedMessage)=>partieDeChasse.Events.Should() .HaveCount(1) .And .EndWith(newEvent(Now, expectedMessage)); ... [Fact]publicvoidEchoueAvecUnChasseurNayantPlusDeBalles() {...var service =newPartieDeChasseService(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 existeif (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 👍.
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.
Lorsqu'on écrit des tests (a priori ou posteriori), il est important d'avoir en tête certains principes tels que les Test Desiderata.