Un framework de test est aux tests ce que l’IDE est au développement. Il fournit un environnement structuré permettant l’exécution de tests et des méthodes pour aider à leur développement.
Vous avez vu dans le chapitre précédent que, non seulement, l’écriture de tests dans l’application console était laborieuse, mais également que, lorsqu’un test échouait, il était difficile d'identifier lequel c’était. De même, nous lancions tous les tests à chaque fois. Peut-être que lors du développement et/ou débogage, nous aurons besoin de n'en lancer qu'un seul.
Les frameworks de tests fournissent également beaucoup d’options pour faire des vérifications. Nous en parlerons dans le chapitre sur les assertions.
Choisissez un framework de test
Il existe plusieurs frameworks de tests, qui s’intègrent très bien avec les dernières versions de Visual Studio (même les versions gratuites).
Nous pouvons citer trois grandes options :
Visual Studio Unit Testing Framework (appelé aussi historiquement MSTest), qui est le framework de Microsoft ;
NUnit qui est un portage de JUnit, le framework de test pour Java ;
xUnit.net, généralement abrégé en xUnit, qui est un framework plus récent, écrit par un des auteurs de NUnit.
Il y a quelques différences subtiles entre les principaux frameworks de tests, mais on peut considérer qu’ils se valent globalement.
Notre programme
Pour la suite de ce cours, nous allons utiliser une petite application qui nous permettra de pratiquer les concepts que nous aborderons. Je vous propose donc de travailler sur un petit jeu. 😏
Il s’agit d’un petit jeu où le héros va combattre une série de monstres. 👻 Il est très simple pour commencer, nous l’améliorerons au fur et à mesure. Et pour sa réalisation, disons que nous ne connaissons pas encore le TDD et que nous avons écrit le code suivant sans aucun test (bouuhhh !! 😜).
C'est parti pour une petite visite guidée de notre application !
Nous avons d'abord la classe Heros
:
public class Heros
{
public int PointDeVies { get; private set; }
public int Points { get; private set; }
public Heros(int pointDeVies)
{
PointDeVies = pointDeVies;
}
public void GagneUnCombat()
{
Points++;
}
public void PerdsUnCombat(int nb)
{
PointDeVies -= nb;
}
}
Plutôt simple : elle encapsule un nombre de points et un nombre de points de vie.
Nous avons également une classe qui sert à faire des lancers de dés :
public class De
{
private Random random;
public De()
{
random = new Random();
}
public int Lance()
{
return random.Next(1, 7);
}
}
Il s’agit donc d’un dé à 6 faces qui utilise la classe Random
du framework .NET pour faire un tirage aléatoire.
Nous avons ensuite une énumération du résultat d’un tour de jeu :
public enum Resultat
{
Gagne,
Perdu
}
Plutôt explicite.
Ensuite, nous avons une classe Jeu
:
public class Jeu
{
public Heros Heros { get; }
public Jeu()
{
Heros = new Heros(15);
}
public Resultat Tour(int deHeros, int deMonstre)
{
if (GagneLeCombat(deHeros, deMonstre))
{
Heros.GagneUnCombat();
return Resultat.Gagne;
}
else
{
Heros.PerdsUnCombat(deMonstre - deHeros);
return Resultat.Perdu;
}
}
private bool GagneLeCombat(int de1, int de2)
{
return de1 >= de2;
}
}
C’est notre classe métier, qui contient la logique de notre jeu. Elle manipule le héros qui démarre avec 15 points de vie.
Nous avons une méthode Tour
qui reçoit deux lancers de dés, un du héros et un du monstre. Nous comparons les deux lancers avec la méthode privée GagneLeCombat
. Si le chiffre du dé du héros est supérieur ou égal à celui du monstre, il remporte le combat valeureusement, avec toute la bravoure d’un héros courageux ! 😄
Dans le cas d’une victoire, le héros est crédité d’un point. Sinon, il perd un nombre de points de vie égal à la différence entre les deux dés.
Voilà donc les classes qui composent notre jeu. Il ne manque plus que la partie interface graphique, qui va être très simpliste, vu que nous utilisons un programme console et qu’il n’y a aucune interaction :
public class Ihm
{
public void Demarre()
{
De de = new De();
var jeu = new Jeu();
Console.WriteLine($"A l'attaque : points/vie {jeu.Heros.Points}/{jeu.Heros.PointDeVies}");
while (jeu.Heros.PointDeVies > 0)
{
var resultat = jeu.Tour(de.Lance(), de.Lance());
switch (resultat)
{
case Resultat.Gagne:
Console.Write($"Monstre battu");
break;
case Resultat.Perdu:
Console.Write($"Combat perdu");
break;
}
Console.WriteLine($": points/vie {jeu.Heros.Points}/{jeu.Heros.PointDeVies}");
}
}
}
Nous instancions le dé, puis le jeu, et tant que le joueur a encore de la vie, nous faisons un tour de jeu. Le programme affiche le résultat du combat ainsi que le nombre de points et les points de vie restants.
Enfin, il faut démarrer tout cela dans la méthode main
:
class Program
{
static void Main(string[] args)
{
var ihm = new Ihm();
ihm.Demarre();
}
}
Ce qui donne un résultat semblable à :
A l'attaque : points/vie 0/15 Monstre battu: points/vie 1/15 Monstre battu: points/vie 2/15 Combat perdu: points/vie 2/14 Combat perdu: points/vie 2/10 Monstre battu: points/vie 3/10 Monstre battu: points/vie 4/10 Monstre battu: points/vie 5/10 Monstre battu: points/vie 6/10 Combat perdu: points/vie 6/9 Monstre battu: points/vie 7/9 Combat perdu: points/vie 7/8 Monstre battu: points/vie 8/8 Monstre battu: points/vie 9/8 Combat perdu: points/vie 9/5 Combat perdu: points/vie 9/0 Appuyez sur une touche pour continuer…
Vous aurez bien sûr un résultat différent, car le dé introduit un résultat aléatoire, ce qui pimente le jeu.
Voilà pour notre application. Dans le chapitre suivant, nous allons voir comment écrire des tests pour ce code.