Utilisez du code pour en tester un autre
Nous avons vu que les tests automatisés sont en fait une portion de code qui permet de tester un autre morceau de code. Naïvement, nous pourrions commencer à écrire un test dans une banale application console.
Imaginons que nous voulions tester une méthode toute simple qui fait l’addition de deux nombres. Par exemple, la méthode suivante :
public static int Addition(int a, int b)
{
return a + b;
}
Faire un test consiste à écrire des bouts de code permettant de s’assurer qu'une autre portion de code fonctionne. Cela peut être, par exemple :
static void Main(string[] args)
{
int a = 1;
int b = 2;
int resultat = Addition(a, b);
if (resultat != 3)
Console.WriteLine("Le test a raté");
else
Console.WriteLine("Le test est ok");
}
Ici, le test passe bien, ouf ! ☺️
Pour être complet, un test doit couvrir un maximum de situations, il faut donc tester notre code avec d’autres valeurs, et ne pas oublier les valeurs limites :
static void Main(string[] args)
{
// premier test
int a = 1;
int b = 2;
int resultat = Addition(a, b);
if (resultat != 3)
Console.WriteLine("Le test a raté");
else
Console.WriteLine("Le test est ok");
// second test
a = 0;
b = 0;
resultat = Addition(a, b);
if (resultat != 0)
Console.WriteLine("Le test a raté");
else
Console.WriteLine("Le test est ok");
// troisième test
a = -5;
b = 5;
resultat = Addition(a, b);
if (resultat != 0)
Console.WriteLine("Le test a raté");
else
Console.WriteLine("Le test est ok");
}
Ici, nous considérons avoir écrit suffisamment de tests pour nous assurer que cette méthode est bien fonctionnelle.
Voilà pour le principe. Plutôt simple, non ?
Et avec le TDD ?
Nous avons parlé de TDD dans la partie précédente. Le principe est de commencer par :
écrire un test ;
le faire passer avec le minimum de code possible ;
remanier le code ;
ajouter un autre test ;
revenir à l'étape 2, jusqu'à ce qu'on considère que tous les scénarios de notre test sont couverts.
Essayons de pratiquer un peu de TDD sur un cas relativement simple et classique, qui est de compter le nombre de mots d’une phrase.
Commençons par écrire un test :
static void Main(string[] args)
{
string phrase1 = "";
if (CompteMots(phrase1) != 0)
Console.WriteLine("Echec du test");
else
Console.WriteLine("Le test est ok");
}
Bien sûr, le test ne passe pas ! Et pour cause, le test ne compile même pas, car aucun code n’a été écrit.
Il faut donc faire passer le test avec le minimum de code possible :
static void Main(string[] args)
{
string phrase1 = "";
if (CompteMots(phrase1) != 0)
Console.WriteLine("Echec du test");
else
Console.WriteLine("Le test est ok");
}
private static int CompteMots(string phrase)
{
return 0;
}
L’implémentation semble absurde, mais le test passe ! Et c’est tout ce que l’on doit faire dans cette étape. Pour l’instant, résistons à la tentation d’aller plus loin. 😉
Il faut désormais passer à l’étape suivante du remaniement. Bon là, cela va être rapide vu le peu de code qu'il y a dans la méthode CompteMots
… 😜 Difficile d’améliorer cela, on ne fait donc rien.
Poursuivons les tests de notre méthode et écrivons un nouveau test. Cette fois-ci, nous allons tester avec un mot :
static void Main(string[] args)
{
string phrase1 = "";
if (CompteMots(phrase1) != 0)
Console.WriteLine("Echec du test");
string phrase2 = "mot";
if (CompteMots(phrase2) != 1)
Console.WriteLine("Echec du test");
}
Comme prévu, le test échoue, il faut donc ajouter du code pour le faire passer :
private static int CompteMots(string phrase)
{
if (phrase.Length == 0)
return 0;
return 1;
}
Les tests passent grâce à cette implémentation minimale, mais fonctionnelle. Nous pouvons remanier le code.
Ici, c’est faisable. Nous pourrions envisager d’écrire cela, par exemple :
private static int CompteMots(string phrase)
{
return phrase.Length == 0 ? 0 : 1;
}
Les tests continuent à passer, nous sommes assurés que le nouveau code n’introduit pas de régression.
Vu la quantité de code, j’aurais tendance à garder la première version, mais c’est une question de goût.
Est-ce que nous avons couvert suffisamment de cas de tests ? Sûrement pas, non ! Alors, écrivons un nouveau test, cette fois-ci avec deux mots. 🙂
static void Main(string[] args)
{
string phrase1 = "";
if (CompteMots(phrase1) != 0)
Console.WriteLine("Echec du test");
string phrase2 = "mot";
if (CompteMots(phrase2) != 1)
Console.WriteLine("Echec du test");
string phrase3 = "deux mots";
if (CompteMots(phrase3) != 2)
Console.WriteLine("Echec du test");
}
Et voilà, nos tests ne passent plus… Il faut écrire du code !
Réfléchissons… Et si on comptait le nombre d’espaces ?
private static int CompteMots(string phrase)
{
if (phrase.Length == 0)
return 0;
int cpt = 0;
foreach (var caractere in phrase)
{
if (caractere == ' ')
cpt++;
}
return cpt + 1;
}
Le test passe ! Parfait. L’idée était bonne !
Est-ce qu'on remanie le code ? Oui, pourquoi pas ? En utilisant les méthodes d’extensions Linq, on pourrait écrire :
private static int CompteMots(string phrase)
{
if (phrase.Length == 0)
return 0;
return phrase.Count(caractere => caractere == ' ') + 1;
}
Est-ce que l’on peut faire ce remaniement sans risque ?
Absolument, oui ! Nous sommes couverts par les tests, ils continuent de passer !
Mais couvrent-ils tout ? Ce n’est pas certain... écrivons un nouveau test :
string phrase4 = "trois petits mots";
if (CompteMots(phrase4) != 3)
Console.WriteLine("Echec du test");
Oui, cela passe ! On tient le bon bout...
Écrivons encore un test :
string phrase5 = " des espaces en trop ";
if (CompteMots(phrase5) != 4)
Console.WriteLine("Echec du test");
Celui-ci échoue. 😒 Eh oui, compter les espaces sans aucune règle, ce n’était pas une si bonne idée.
Améliorons le code, en utilisant Split pour découper la phrase sur les espaces et en utilisant Trim()
pour nettoyer les espaces inutiles dans les mots :
private static int CompteMots(string phrase)
{
if (phrase.Length == 0)
return 0;
var mots = phrase.Split(' ');
int cpt = 0;
foreach (var mot in mots)
{
if (mot.Trim().Length > 0)
cpt++;
}
return cpt;
}
Chouette, cela passe. Superbe algorithme ! 😄
C’est parti pour le remaniement !
Super algorithme, mais… est-ce que le Trim()
sert à quelque chose ? Vérifions cela en le supprimant :
private static int CompteMots(string phrase)
{
if (phrase.Length == 0)
return 0;
var mots = phrase.Split(' ');
int cpt = 0;
foreach (var mot in mots)
{
if (mot.Length > 0)
cpt++;
}
return cpt;
}
Les tests passent, donc non, cela ne servait à rien.
Et le premier test, sur la longueur du mot égale à 0 ?
Eh bien, en fait, il ne sert pas non plus. Je peux faire ce nettoyage avec mon filet de sécurité sans aucun souci. Je tente un petit remaniement supplémentaire pour utiliser les méthodes d’extensions Linq :
private static int CompteMots(string phrase)
{
var mots = phrase.Split(' ');
return mots.Count(mot => mot.Length > 0);
}
C’est quand même plus clair, non ? Et puis cela continue à fonctionner : ce n’est pas moi qui le dis, ce sont les tests. 🙂
Je pense que vous avez compris le principe. Il faudrait vous assurer également que les tests aux limites fonctionnent, par exemple le test suivant va échouer (et même faire planter l’application !) :
string phrase6 = null;
if (CompteMots(phrase6) != 0)
Console.WriteLine("Echec du test");
Voilà donc un exemple de TDD. Bien sûr, la méthode est ici poussée au maximum pour que vous compreniez l’intérêt de cette pratique.
Cependant, vous avez maintenant plusieurs tests écrits qui vous assurent du bon fonctionnement du code. Et puis le fait d’avoir remanié le code plusieurs fois n’a pas été si long et a permis de faire émerger la solution au fur et à mesure.
Vous verrez qu’il y a toujours des premiers essais qui satisfont les tests, mais qu’il sera possible d’améliorer régulièrement notre code. Cela devient possible grâce aux tests qui nous garantissent que tout continue à bien fonctionner.
La pratique du TDD dépend de la façon dont le développeur appréhende sa philosophie de développement. Elle est présentée ici pour sensibiliser à cette pratique, mais son utilisation, bien que fortement recommandée, n’est pas obligatoire.
Voilà pour les tests basiques. Cependant, utiliser une application console pour faire ses tests, ce n’est pas très pratique ! Vous en conviendrez. Nous avons besoin d’outils ! Nous allons les découvrir dans le prochain chapitre. 😉