Les tests unitaires
Lorsqu'on s'intéresse aux tests qu'effectuent les développeurs au quotidien, on constate que la stratégie la plus répandue consiste à truffer le code de traces temporaires, lancer l'application, appuyer sur quelques boutons pour "remuer" la fonctionnalité en cours de développement, et enfin vérifier dans les traces le bon comportement de l'ensemble. Cette stratégie est malheureusement loin d'être efficace pour au moins deux raisons :
- La mise en place du contexte de test, l'éxécution proprement dite et l'interprétation des résultats sont trop coûteux en temps. Les développeurs vont y passer le moins de temps possible, surtout - et c'est paradoxal - à l'approche des livraisons, quand la nécessité de faire vite l'emporte sur la nécessité de faire bien.
- Les tests ne portent en général que sur la fonctionnalité en cours de développement, et trop peu de régressions sont ainsi détectées.
L'Extreme Programming propose une solution pour améliorer cette situation, sous la forme des tests unitaires ou de la programmation pilotée par les tests : pour chaque classe de l'application, les développeurs écrivent une classe chargée de la tester et de vérifier le résultat de ces tests de manière automatique, c'est-à-dire sans intervention du développeur.
Par rapport aux tests manuels évoqués précédemment, la granularité des tests unitaires est extrêmement fine - ils portent en effet sur des portions de quelques lignes de code uniquement. La question se pose alors de leur validité : comment peuvent-ils garantir le comportement d'ensemble de l'application ?
En fait, l'intérêt des tests unitaires n'est pas là. Dans un projet XP, ce rôle est pris en charge par d'autres types de tests, appelés tests fonctionnels ou tests de recette, qui valident chaque fonctionnalité de l'application dans son ensemble et permettent ainsi de mesurer l'avancement du projet. Le tableau suivant précise les différences entre ces deux types de tests :
|
Tests
unitaires
|
Tests
de recette
|
|
| Ecrits par... | Les développeurs | Le client ou son représentant |
| Portent sur... | Des méthodes unitaires | L'ensemble de l'application |
| Approche... | Boîte blanche | Boîte noire |
| Concernent le client | Non | Oui |
Les tests de recette sont clairement plus intéressants dans l'absolu, mais ils sont généralement assez coûteux à mettre en oeuvre. A l'inverse, les tests unitaires sont beaucoup plus légers :
- La mise en place du contexte de test et la vérification du résultat sont le plus souvent très simples, et le coût d'écriture des tests est très réduit.
- Ils peuvent être exécutés très tôt dans le développement d'une tâche (les tests de recette ne peuvent être lancés que lorsque la fonctionnalité complète est implémentée).
- Ils peuvent être lancés à chaque compilation de la classe (les tests de recette ne peuvent être lancés que lorsque toute l'application est compilée).
Les tests unitaires apportent donc un feedback beaucoup plus rapide que les tests de recette, pour un coût nettement plus réduit. XP exploite cet avantage à l'extrême en proposant d'écrire les tests unitaires AVANT le code proprement dit. Cela permet d'explorer à la fois le problème et la conception avant de passer à l'implémentation, mais cela permet surtout de savoir exactement quand s'arrêter : on arrête de développer une méthode ou une classe lorsque tous ses tests passent.
En pratique, c'est surtout ce dernier point qui nous fait apprécier les tests unitaires. Ils apportent un sentiment d'avancement concret, et le fait d'avoir plusieurs fois par jour l'ensemble des tests unitaires qui passe à 100% nous donne confiance dans le code et nous permet d'avancer rapidement.
Bien sûr, l'adoption de cette pratique n'est ni gratuite ni immédiate. Il faut d'abord apprendre à utiliser un framework de tests, et surtout ensuite apprendre à organiser son code pour le rendre testable unitairement. L'objectif de ce dossier est de faciliter cet apprentissage en montrant :
- Un exemple simple de mise en oeuvre de la démarche,
- Les bases de l'utilisation d'un framework de tests,
- Quelques tactiques habituelles de test.