juin
2009
Le TDD ou Test Driven Development est une méthode permettant le développement d’une application par les tests, au lieu d’écrire le source et le tester a la fin du développement, le TDD préconise d’écrire les tests unitaires avant d’écrire le code source.
Comment ca marche ?
Un TDD s’écrit en 6 étapes :
- écrire un test
- vérifier qu’il échoue (car le code que l’on teste n’existe pas)
- écrire juste le code suffisant pour passer le test
- vérifier que le test passe
- Checkiner le source pour que les autres développeurs puissent l’utiliser
- Refactoriser le code
Mais pourquoi faire ?
Ces tests permettent de préciser les spécifications du code, et donc son comportement ultérieur en fonction des situations auxquelles il sera exposé. Ce qui facilite la production d’un code valide en toutes circonstances. On obtient donc un code plus juste et plus fiable.
En écrivant les tests d’abord, on utilise le programme avant même qu’il existe. On s’assure ainsi que le code produit est testable unitairement. Il est donc impératif d’avoir une vision précise de la manière dont on va utiliser le programme avant même d’envisager son implémentation. Cela évite souvent des erreurs de conception dues à une précipitation dans l’implémentation avant d’avoir défini les objectifs.
De plus, le fait d’avoir des tests augmente la confiance en soi du programmeur lors de la refactorisation du code : il sait qu’à un moment donné les tests ont réussi. Il peut ainsi se permettre des changements radicaux de design en étant sûr, à la fin, d’avoir un programme se comportant toujours de la même façon (si les tests réussissent toujours).
L’utilisation de TDD permet la construction conjointe du programme et d’une suite de tests de non-régression.
Entrons dans le vif du sujet
Prenons un exemple,
Notre analyste nous demande de transformer le prix HT dans l’application par le prix TTC.
Nous devons donc réaliser une fonction qui transforme un prix HT en prix TTC.
Travaillons par étape, en premier nous devons créer un projet de tests unitaires.
Puis nous créons une class de tests unitaires pour tester la class Price
Le nom de la class de test pourra être appelée PriceTest par exemple.
Il est très important de définir une norme pour le nom des projets de test, les class de test, et les méthodes de test entre tous les développeurs pour une homogénéité du code (StyleCop)
Que doit faire ce test ?
la réponse est relativement simple, il doit créer un objet de type Price qui a une valeur correspondant au prix HT, et ajouter une taxe a ce prix.
Quels sont les acteurs, les activités et les asserts du test ?
Acteurs :
- Un object Price
- Le prix HT
- Le prix TTC retourné par la fonctionnalité
- Le prix TTC attendu
Activités :
- Appeler une method de la class Price qui nous retourne un decimal de la valeur TTC
Asserts :
- Vérifier que la valeur attendu est égale à la valeur retournée par la methode
Prenons un scénario, si le prix HT est 50 Euros alors quelle sera la valeur TTC ?
Durant l’analyse de ce scénario et l’écriture de ce test, plusieurs questions restent sans réponses :
- Qu’elle est la valeur de la taxe
- Combien de chiffre après la virgule pour le prix TTC
- Doit on arrondir ou couper le prix TTC
Ces questions doivent être répondu par notre analyste le plus rapidement possible.
Il nous répond que la taxe sera exclusivement de 19.6 et que l’on ne doit pas arrondir la valeur TTC.
Ce qui donne le test suivant :
[TestMethod]
public void Price_PriceTTCIs59_8_WhenPriceHTIs50()
{
#region Actors
Price price;
decimal HTPrice = 50;
decimal TTCPrice;
decimal expectedTTCPrice = 59.8m;
#endregion#region Activities
price = new Price(HTPrice);
TTCPrice = price.GetTTCPrice();
#endregion#region Asserts
Assert.AreEqual(expectedTTCPrice, TTCPrice, « The TTC expected price is not correct »);
#endregion
}
Le test ne compile pas car la class Price n’existe pas encore.
Faisons une implémentation rapide de cette class.
public class Price
{
private decimal _price;
public Price(decimal price)
{
_price = price;
}public decimal GetTTCPrice()
{
return 59.8m;
}
}
Le test est vert, le code peut être checkiner pour que les autres développeurs puissent l’utiliser pour l’interface graphique par exemple.
Maintenant nous pouvons prendre un peu de temps pour implémenter la methode GetTTCPrice;
public decimal GetTTCPrice()
{
decimal TTCPrice;
TTCPrice = _price * 1.196m;return TTCPrice;
}
Le test reste vert la methode GetTTCPrice a l’air de fonctionner.
Ecrivons un 2 eme test pour valider notre implémentation
[TestMethod]
public void Price_PriceTTCIs119_6_WhenPriceHTIs100()
{
#region Actors
Price price;
decimal HTPrice = 100;
decimal TTCPrice;
decimal expectedTTCPrice = 119.6m;
#endregion#region Activities
price = new Price(HTPrice);
TTCPrice = price.GetTTCPrice();
#endregion#region Asserts
Assert.AreEqual(expectedTTCPrice, TTCPrice, « The TTC expected price is not correct »);
#endregion
}
Les 2 tests sont vert, tout va bien, nous pouvons checkiner notre code.
Bravo nous venons de faire du TDD.
Oui, tu as surment raison, j’aurais du peut-etre plus insister sur ce point important.
Vi, j’ai bien vu, mais je parlais de l’exemple qui allait avec
c’est bien ce que j’ai dit.
* écrire un test
* vérifier qu’il échoue (car le code que l’on teste n’existe pas) RED
* écrire juste le code suffisant pour passer le test
* vérifier que le test passe GREEN
* Checkiner le source pour que les autres développeurs puissent l’utiliser
* Refactoriser le code Refactor
> Bravo nous venons de faire du TDD.
Euh….Non…
Le TDD, c’est Red-Green-Refactor…
La, il te manque une étape
(bon, ok, c’est du tirage de cheveux, mais tant qu’a faire un article niveau intro, autant bien placer les choses des le début :D)