Alors, nous avons Ă©crit (ou reçu) un code complet, qui semble simple et fonctionnel. Mais je suis sĂ»r que, comme moi, vous dĂ©bordez dâidĂ©es pour lâamĂ©liorer afin de crĂ©er la futur "killer app" qui nous rendra tous millionnaires. đ
Sauf que, si nous nous lançons tĂȘte baissĂ©e dans un nouveau dĂ©veloppement, nous faisons peser au-dessus de nos tĂȘtes une insidieuse Ă©pĂ©e de DamoclĂšs prĂȘte Ă nous tomber dessus dĂšs la moindre rĂ©gression. đ±
Vous avez envie de garder votre tĂȘte ? Cela tombe bien, moi aussi !
Je vous propose donc dâĂ©crire des tests unitaires. DĂ©couvrons tout de suite MSTest.
Pour pouvoir utiliser MSTest, la premiĂšre chose Ă faire est dâajouter un projet de tests unitaires Ă notre application. Pour ce faire, utilisez le clic-droit sur la solution pour ajouter un nouveau projet :

Et choisissez un projet de type test unitaire (.NET Framework) :

Jâai donnĂ© le nom Jeu2.UnitTests comme nom de projet afin de rendre explicite le fait que ce soit un projet de test, qui teste le  Jeu2 de maniĂšre unitaire.
Vous pouvez voir que Visual Studio nous génÚre un squelette de classe :
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
}
}Nous pouvons déjà y voir un attribut  [TestClass] au-dessus de la définition de la classe.
Il sâagit dâun attribut spĂ©cifique au framework de test MSTest qui permet de marquer la classe comme Ă©tant une classe contenant des tests automatisĂ©s. Cela va permettre au framework dâidentifier les tests quâil devra exĂ©cuter.
Lâattribut  [TestMethod] fait Ă peu prĂšs la mĂȘme chose et permet dâindiquer que la mĂ©thode est une mĂ©thode de test.
Ouvrez la fenĂȘtre de lâexplorateur de tests. Si vous ne la voyez pas, il faut aller dans le menu Test, puis FenĂȘtres, Explorateur de tests :

Vous verrez alors notre test, qui sâappelle TestMethod1 , avec un point dâexclamation bleu indiquant quâil nâa pas encore Ă©tĂ© exĂ©cutĂ© :

Vous pouvez cliquer sur âExĂ©cuter toutâ afin de dĂ©marrer tous les tests ; vous aurez :

La coche verte indique bien sûr que votre test passe, ce qui est le but à atteindre ; la terre promise de tout développeur !
Bon, vu qu'on ne teste rien pour l'instant, cela ne peut que passer. âșïž
Modifiez la méthode de test pour avoir :
[TestMethod]
public void TestMethod1()
{
throw new NotImplementedException();
}Relancez les tests, et vous aurez cette fois :

Cette fois-ci - et de maniĂšre tout Ă fait surprenante -, la croix rouge indique que le test a Ă©chouĂ©. Si lâon clique sur le test, nous aurons Ă©galement des dĂ©tails sur les raisons de lâĂ©chec. En l'occurrence, nous avons eu ici une exception.
ànoter que nous pouvons également démarrer nos tests en mode debug, ce qui est trÚs pratique pour analyser un test qui est en échec :

Sachez que  [TestClass] et  [TestMethod] ne sont pas les seuls attributs utilisables avec le framework MSTest. Il en existe plusieurs, et pour vous donner envie de les découvrir, laissez-moi vous en présenter deux autres : les attributs  TestInitialize et  TestCleanup .
Ils permettent de décorer des méthodes qui seront appelées respectivement avant et aprÚs chaque test. C'est l'endroit idéal pour factoriser des initialisations ou des nettoyages dont dépendent tous les tests :
[TestClass]
public class UnitTest1
{
[TestInitialize]
public void InitialisationDesTests()
{
// ajouter les initialisations
}
[TestMethod]
public void MonTest()
{
// test Ă faire
}
[TestCleanup]
public void NettoyageDesTests()
{
// nettoyer les variables, ...
}
}Vous pouvez retrouver les autres attributs sur cette page ; ce sont les classes qui finissent par  Attribute .
Nous sommes presque prĂȘts Ă Ă©crire un test pour notre jeu. Mais comment lâĂ©crire proprement ?
En gĂ©nĂ©ral, un test se dĂ©compose en trois partie, suivant le schĂ©ma « AAA », qui correspond aux mots anglais « Arrange, Act, Assert », que lâon peut traduire en français par « Arranger, Agir, Auditer ».
Arranger : il sâagit dans un premier temps de dĂ©finir les objets, les variables nĂ©cessaires au bon fonctionnement de son test (initialiser les variables, initialiser les objets Ă passer en paramĂštres de la mĂ©thode Ă tester, etc.).
Agir : ensuite, il sâagit dâexĂ©cuter lâaction que lâon souhaite tester (en gĂ©nĂ©ral, exĂ©cuter la mĂ©thode que lâon veut tester, etc.)
Auditer : et enfin de vérifier que le résultat obtenu est conforme à nos attentes.
Il est temps de pratiquer un peu et pour cela, nous allons écrire une méthode qui teste le tour de jeu ; à savoir la méthode  Tour de la classe  Jeu . Le but est de vérifier que le héros gagne bien si son lancer de dés est supérieur à celui du monstre ; et inversement, que le joueur perd des points de vie lorsque le lancer de dés du monstre est meilleur que celui du héros.
Nous pouvons écrire :
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
// Arrange
Jeu jeu = new Jeu();
// Act
var resultat = jeu.Tour(6, 1);
// Assert
if (resultat != Resultat.Gagne)
Assert.Fail();
if (jeu.Heros.Points != 1)
Assert.Fail();
if (jeu.Heros.PointDeVies != 15)
Assert.Fail();
}
[TestMethod]
public void TestMethod2()
{
// Arrange
Jeu jeu = new Jeu();
// Act
var resultat = jeu.Tour(5, 5);
// Assert
if (resultat != Resultat.Gagne)
Assert.Fail();
if (jeu.Heros.Points != 1)
Assert.Fail();
if (jeu.Heros.PointDeVies != 15)
Assert.Fail();
}
[TestMethod]
public void TestMethod3()
{
// Arrange
Jeu jeu = new Jeu();
// Act
var resultat = jeu.Tour(2, 4);
// Assert
if (resultat != Resultat.Perdu)
Assert.Fail();
if (jeu.Heros.Points != 0)
Assert.Fail();
if (jeu.Heros.PointDeVies != 13)
Assert.Fail();
}
}Vous remarquerez à chaque fois les trois phases AAA, illustrées avec le commentaire. Ce schéma vaut pour tous les tests.
La mĂ©thode  TestMethod1 permet de sâassurer que, si le joueur a un dĂ© dont la valeur est supĂ©rieure Ă celle du dĂ© de lâadversaire, alors il gagne. Il a un point et toujours 15 points de vie.
De la mĂȘme façon,  TestMethod2 vĂ©rifie des Ă©lĂ©ments similaires avec un mĂȘme jet de dĂ©s. Enfin,  TestMethod3 sâassure que le hĂ©ros est bien capable de perdre.
Vous pouvez jouer la suite de tests et vous aurez les trois tests qui passent :

Avoir les tests qui passent est trĂšs bien, mais ce nâest pas suffisant. Il faut dĂ©sormais modifier les valeurs de vĂ©rification pour sâassurer que les tests peuvent Ă©galement Ă©chouer et quâil nây a pas de faux positifs.
Il arrive en effet que des tests ne vérifient rien du tout et en conséquence, soient verts.
Par exemple, le test suivant :
[TestMethod]
public void TestMethod4()
{
// Arrange
Jeu jeu = new Jeu();
// Act
var resultat = jeu.Tour(2, 4);
}Vous voyez, jâexĂ©cute du code, mais je ne vĂ©rifie rien. Donc le test est Ă©galement vert. Sauf que ce test est un faux positif qui vous fait croire que le test est fonctionnel, alors quâen vrai, rien nâest testĂ©. Supprimez-moi vite ce test contreproductif ! đ
Pour ce faire, modifions par exemple  TestMethod1 pour avoir :
[TestMethod]
public void TestMethod1()
{
// Arrange
Jeu jeu = new Jeu();
// Act
var resultat = jeu.Tour(6, 1);
// Assert
if (resultat != Resultat.Perdu)
Assert.Fail();
if (jeu.Heros.Points != 1)
Assert.Fail();
if (jeu.Heros.PointDeVies != 15)
Assert.Fail();
}Ă la ligne 11, jâai changĂ© le test sur le rĂ©sultat de la mĂ©thode  Tour() . AprĂšs avoir relancĂ© les tests, je peux constater que le test Ă©choue bien :

Il a donc un intĂ©rĂȘt. Il ne vous reste plus quâĂ vous empresser de remettre le test en Ă©tat. đ
AAA nâest pas le seul schĂ©ma qui existe pour structurer ses tests. Given When Then (que lâon peut traduire par : âĂ©tant donnĂ© que ⊠lorsque ⊠alors âŠ) est un schĂ©ma issu de la mĂ©thodologie BDD qui est souvent considĂ©rĂ©e comme une Ă©volution de TDD.
LâidĂ©e est que le test puisse devenir comprĂ©hensible par des non-dĂ©veloppeurs grĂące Ă une formulation reprĂ©sentant le scĂ©nario Ă tester.
Given : dĂ©crit lâĂ©tat du systĂšme avant que lâon dĂ©marre le comportement du scĂ©nario Ă tester. Il sâagit des prĂ©conditions du test.
When : correspond au comportement que lâon veut tester.
Then : sert Ă dĂ©crire les changements que lâon attend Ă la suite de lâexĂ©cution du comportement.
Vous voyez, ces trois phases sont assez proches de AAA.
En gĂ©nĂ©ral, le schĂ©ma qui est retenu comporte une phase supplĂ©mentaire en fin de test, oĂč l'on va remettre le systĂšme dans son Ă©tat initial, tel quâil Ă©tait avant lâexĂ©cution du test.
Bon, avez-vous bien étudié nos précédents tests ? Ils vous paraissent bien ? Ne manque-t-il pas un petit quelque chose pour en faire de super tests ?
Je vous ai dĂ©jĂ indiquĂ© quâil faut nommer correctement ses projets de tests, mais ceci est valable Ă©galement pour les classes de tests, ainsi que pour les mĂ©thodes de tests.
Vous serez dâaccord avec moi pour dire que  TestMethod1 nâest pas un nom trĂšs explicite.
Il faut que vous fassiez lâeffort de nommer correctement vos tests pour en rendre la lecture et la maintenance plus faciles.
Commençons par la classe de test. Il n'y a pas de rĂšgle de nommage obligatoire, mais il est intĂ©ressant dâavoir une norme pour sây retrouver facilement. Je vous propose de nommer les classes de tests en commençant par le nom de la classe que lâon doit tester, suivie du mot Tests. Ce qui donne :  JeuTests .
Et pour les méthodes de tests ?
Ici aussi, il est intĂ©ressant de suivre une rĂšgle de nommage afin de pouvoir identifier rapidement lâintention de la mĂ©thode de test. Je vous propose un nommage de la forme suivante :
MethodeTestee_EtatInitial_EtatAttendu()
Nous pourrions donc renommer  TestMethod1 en :
public void Tour_AvecUnDeSuperieurAuSecond_RetourneGagneAvecUnPointEtSansPerdreDePointsDeVie()
{
}Il est vrai que le nom de la méthode est un peu long, mais vous me remercierez plus tard quand cela vous aura fait gagner un temps précieux.
Ă noter que certains lanceurs de tests peuvent faire apparaĂźtre une description, saisie grĂące Ă un attribut :
[TestMethod]
[Description("Etant donné un tour de jeu, lorsque j'ai un lancer supérieur au second, alors le résultat est gagné avec un point sans perdre de points de vie")]
public void Tour_AvecUnDeSuperieurAuSecond_RetourneGagneAvecUnPointEtSansPerdreDePointsDeVie()
{
}Et voilà . Vous commencez à découvrir le framework de test MSTest. Continuons notre chemin et passons aux assertions.