Avant de parler de tests dâintĂ©gration, je voudrais vous parler un peu de couverture de code. Plus vous allez Ă©crire de tests et plus vous aurez besoin dâavoir des indicateurs sur ce qui est testĂ©. La mesure de la couverture de code est un indicateur intĂ©ressant que tout dĂ©veloppeur qui se respecte doit connaĂźtre.
La couverture de code est une mesure utilisĂ©e pour dĂ©terminer le taux de code source exĂ©cutĂ© lorsquâune suite de tests est lancĂ©e. Pour essayer de limiter les bugs, les tests doivent couvrir une large proportion de code.
Il y a plusieurs indicateurs de couverture de code :
Couverture des fonctions (ou méthodes) : est-ce que toutes les méthodes du code ont été appelées par les tests ?
Couverture des instructions : est-ce que les tests sont passés sur chaque ligne de code ?
Couverture des chemins dâexĂ©cution : est-ce que lâon est passĂ© dans toutes les branches de notre code ? Par exemple, lâinstruction  if gĂ©nĂšre deux branches de code : une dans laquelle la condition Ă©valuĂ©e est vraie, une autre oĂč la condition est fausse.
Couverture des points de tests : est-ce que chaque condition sur le test dâune variable a Ă©tĂ© couverte ?
En gĂ©nĂ©ral, la plupart des outils proposent la couverture des instructions et des chemins dâexĂ©cution. Et la mĂ©trique la plus fiable est celle des chemins dâexĂ©cution, câest celle que vous devez privilĂ©gier.
Il existe plusieurs outils permettant de mesurer la couverture de code. Nous allons passer en revue les plus connus.
Visual Studio possĂšde un outil de mesure de code.
Par contre, il nâest utilisable quâĂ partir de lâĂ©dition professionnelle de Visual Studio, qui est payante (contrairement Ă la version community, que jâutilise ici, qui est gratuite). Si vous avez une version payante de Visual Studio, et câest trĂšs souvent le cas en entreprise, nâhĂ©sitez pas Ă utiliser cette fonctionnalitĂ© de couverture de code. Vous trouverez plus dâinformations ici.
Il existe aussi DotCover, qui est lâoutil de Jetbrains.
Câest un logiciel payant Ă©galement, mais certaines entreprises achĂštent des licences de Resharper.
DotCover est inclus avec certaines licences des produits JetBrains. NâhĂ©sitez pas Ă vĂ©rifier si vous utilisez dĂ©jĂ un autre logiciel de JetBrains, vous aurez peut-ĂȘtre accĂšs Ă DotCover. Vous trouverez plus dâinformations ici.
Un autre outil assez connu est NCover.
Toujours payant, câest un outil qui existe depuis trĂšs longtemps et que lâon retrouve parfois associĂ© Ă dâautres logiciels. Suivez ce lien si vous souhaitez avoir plus dâinformations.
Et je terminerai par une derniĂšre option : OpenCover.
Il sâagit dâun outil open source sous licence MIT. Câest celui que nous allons utiliser dans ce cours, sachant quâil ne fonctionne que sous Windows et que nous allons donc installer Ă©galement des extensions pour pouvoir lâutiliser correctement. Le projet se trouve sur github, mais vous n'avez pas besoin de le cloner, il suffira de rĂ©fĂ©rencer le bon package Nuget.
Pour utiliser OpenCover, il faut commencer par référencer notre package Nuget, directement dans notre projet de test :

Il faut ensuite trouver OpenCover et lâinstaller :

OpenCover est le moteur dâanalyse de la couverture de code. Il nous faut maintenant une interface permettant de lire les rĂ©sultats de lâanalyse. Avec Visual Studio 2017, nous pouvons installer lâextension AxoCover :

Une fois lâextension tĂ©lĂ©chargĂ©e, vous pouvez lâinstaller :

Une fois lâinstallation terminĂ©e, redĂ©marrez Visual Studio et rouvrez votre solution.
Vous avez désormais un nouvel élément dans le menu outils :

Si vous cliquez dessus, une nouvelle fenĂȘtre s'ouvre :

Normalement, nous y voyons nos tests.
Il ne vous reste plus quâĂ cliquer sur Cover pour dĂ©marrer les tests et lâanalyse.
AprĂšs lâexĂ©cution, vous pouvez constater lâapparition dâun nouvel onglet Report :

Cliquez dessus pour voir les résultats de la couverture de code :

Nous pouvons voir plein de petites choses. đ
Vous voyez notamment les noms des namespaces et des classes, ainsi que deux pourcentages à leurs cÎtés.
Le premier correspond au pourcentage des branches qui sont couvertes et le second au pourcentage de lignes couvertes. Par exemple, nous pouvons voir que la classe  Heros est couverte Ă 100 %, alors que la classe  Ihm nâest couverte quâĂ 87,5 % au niveau des chemins dâexĂ©cution.
Vous voyez aussi que les classes  De ,  FournisseurMeteo et  ConsoleDeSortie sont à 0 %.
Eh oui, lors des tests, nous avons remplacé ces dépendances aléatoires par de fausses implémentations...
Si nous cliquons sur la classe  Ihm , nous avons des prĂ©cisions supplĂ©mentaires sur lâanalyse de cette classe :Â

Nous voyons donc que nous avons une branche qui nâest pas couverte par nos tests et qu'elle est dans la mĂ©thode  Demarre() .
Mince ! Mais quelle est cette branche non couverte et qui va prendre froid ? đ
Pour le savoir, il suffit de cliquer sur Source - Ă droite - et nous voilĂ redirigĂ©s dans le code de la classe. Mais il nâest plus tout Ă fait comme avant. Sur la gauche, nous avons des couleurs :

Un trait rouge indique quâune ligne (ou plusieurs) nâest pas couverte. Vous le voyez, câest le cas pour le contenu de la mĂ©thode  Main . Câest bien normal, Ă©tant donnĂ© que nous nâavons aucun test qui appelle ce bout de code. Ăvidemment, la ligne verte indique que la ligne est couverte.
Pour les branches, câest un petit peu plus subtil, car il sâagit dâun petit cercle sur la gauche :

Lorsque tout est vert, câest que toutes les options sont couvertes. Typiquement, pour la condition de la boucle  while, il faut avoir un cas oĂč elle est vraie et un cas oĂč elle est fausse, ce qui est le cas dans nos tests.
Pour le  switch - et câest ce qui explique notre score de 85,7 % -, nous nâavons pas gĂ©rĂ© de cas par dĂ©faut. En fait, câest comme si nous avions Ă©crit ce code :

Nous voyons bien, sur cette capture dâĂ©cran, avec le rĂ©sultat de la couverture de code, que nous ne passons jamais dans le cas  default . En effet, nous nâavons que 2 valeurs dans notre Ă©numĂ©ration, difficile de se retrouver dans le cas dâune valeur qui nâexiste pas !
On pourrait considĂ©rer ici que lâoutil de couverture de code nous remonte un faux positif, car dans notre code, nous avons couvert toutes les possibilitĂ©s. Mais techniquement, il pourrait ĂȘtre envisageable dâavoir dâautres options. Une  enum nâest rien de plus quâun entier, finalement. Le tour de jeu pourrait trĂšs bien renvoyer ceci Ă un moment ou Ă un autre :
public Resultat Tour(int deHeros, int deMonstre)
{
return (Resultat)3;
}Ce code compile et fonctionne. Mais alors, ce cas nâest pas traitĂ© par notre premiĂšre version du code. En revanche, avec la seconde version, nous aurons une exception levĂ©e pour cette valeur qui nâest pas gĂ©rĂ©e explicitement.
Donc, vous avez deux options :
Il faut écrire un nouveau test pour gérer ce cas-là .
Cela nĂ©cessiterait dâextraire une interface de la classe  Jeu , de rĂ©cupĂ©rer la dĂ©pendance dans le constructeur et dâutiliser un bouchon ou un simulacre pour renvoyer une valeur qui ne fait pas partie de lâĂ©numĂ©ration. Et lĂ , cela nous fait pas mal de travail pour pas grand-choseâŠ
Lâautre option est de réécrire le premier code pour avoir un  default à la place du  Resultat.Perdu (ligne 6) :
switch (resultat)
{
case Resultat.Gagne:
_console.Ecrire($"Monstre battu");
break;
default:
_console.Ecrire($"Combat perdu");
break;
}Avec ce code-lĂ , la couverture de code repasse Ă 100 % pour la classe  Ihm . Chouette ! đ
Nous venons de voir que la mesure peut ĂȘtre diffĂ©rente en fonction de la façon dont nous avons Ă©crit notre code. Cependant, on pourrait se demander si la réécriture prĂ©sentĂ©e ci-dessus est bien utile.
En effet, nos tests couvraient avec succĂšs tous les cas fonctionnels de notre tour de jeu. Alors, faut-il nous obstiner Ă modifier le code et passer de 85 % Ă 100 % ?
Câest lĂ qu'il faut ĂȘtre capable de se rendre compte de ce quâest vraiment cet indicateur de couverture de code.
Pour quâil puisse vous ĂȘtre utile, il faut bien comprendre comment il fonctionne et ce quâil nous dit vraiment derriĂšre les chiffres.
On pourrait penser que plus le pourcentage est élevé, plus le code est de qualité.
Quâen pensez-vous ?
Câest la premiĂšre chose qui vient Ă lâesprit. En consĂ©quence, il paraĂźtrait logique de vouloir atteindre 100 % de couverture de code.
Pour nous en convaincre, prenons un exemple simpliste :
[TestClass]
public class ExempleTest
{
[TestMethod]
public void Test1()
{
StringHelper.EstCeQueLaChaineEstLongue("abc");
}
}
public static class StringHelper
{
public static bool EstCeQueLaChaineEstLongue(string chaine)
{
if (chaine.Length > 10)
return true;
else
return false;
}
}Le code parle de lui-mĂȘme, on veut savoir si une chaĂźne est longue ou pas.
Oui, je dĂ©cide arbitrairement quâune chaĂźne est longue si elle dĂ©passe dix caractĂšres. đ
Notre test appelle la méthode avec une chaßne courte ; nous avons donc une couverture de code de 66,67 % au niveau des branches, et de 80 % au niveau des lignes de code :

En effet, nous ne sommes pas passĂ©s par le cas oĂč la chaĂźne est longue.
La logique voudrait que nous ajoutions un cas de test, cette fois-ci avec une chaßne longue. Mais soyons fous et changeons plutÎt le code de la méthode à tester :
public static bool EstCeQueLaChaineEstLongue(string chaine)
{
return chaine.Length > 10;
}Maintenant, relançons lâanalyse. Nous obtenons :

Nous avons cette fois-ci une couverture de code Ă 100 % . đČ Pourtant, le code fait exactement la mĂȘme chose que prĂ©cĂ©demment ! Il nous manque toujours un cas de test, mais lâindicateur dit bien 100 %.
Câest grave, non ?
Mais il y a pire que ça ! Vous voyez ? Oui, jâespĂšre que vous avez vu. đ
En vĂ©ritĂ©, avec ce test, nous ne testons rien. Nous avons simplement dĂ©marrĂ© une mĂ©thode. Le code correct de test aurait dĂ» ĂȘtre dans ce cas, a minima :
StringHelper.EstCeQueLaChaineEstLongue("abc").Should().BeFalse();Ici, nous vérifions un résultat, il y a une assertion.
Alors, que pouvez-vous en penser ?
Nous avons vu que lâindicateur de couverture de code Ă©tait Ă 100 %, alors que tous les scĂ©narios de tests nâexistaient pas et que le test ne testait finalement rien.
Je vous lâaccorde, mon exemple est extrĂȘme. Mais son objectif est de vous faire comprendre quâun haut pourcentage de couverture de code nâest pas forcĂ©ment synonyme de qualitĂ© de test. Cet indicateur fournit une tendance, il faut lâutiliser sur la durĂ©e.
En gĂ©nĂ©ral, si l'on a peu de couverture de code, câest un indicateur pour rajouter des tests. Au contraire, avoir une grosse couverture de code ne veut pas forcĂ©ment dire que tout est bien testĂ© et que les tests sont de qualitĂ©.
Il faut aussi rester raisonnable et pragmatique. Le Graal de 100 % de couverture de code est en gĂ©nĂ©ral impossible Ă atteindre. Sur un vrai projet, il demande bien souvent trop dâefforts (et donc un coĂ»t trop important) par rapport Ă lâintĂ©rĂȘt dâavoir une telle couverture. Dâautant plus que nous venons de voir que ce taux ne garantit pas la qualitĂ©.