Test unitaire

Un article de Wikipédia, l'encyclopédie libre.
Aller à : navigation, rechercher
Page d'aide sur l'homonymie Pour les articles homonymes, voir Test.

En programmation informatique, le test unitaire (ou « T.U. », ou « U.T. » en anglais) est une procédure permettant de vérifier le bon fonctionnement d'une partie précise d'un logiciel ou d'une portion d'un programme (appelée « unité » ou « module »).

Dans les applications non critiques, l'écriture des tests unitaires a longtemps été considérée comme une tâche secondaire. Cependant, les méthodes Extreme programming (XP) ou Test Driven Development (TDD) ont remis les tests unitaires, appelés « tests du programmeur », au centre de l'activité de programmation.

Origine[modifier | modifier le code]

L'environnement de test sUnit[1] pour le langage Smalltalk, créé en octobre 1994 par Kent Beck[2]. En 1997, Kent Beck rencontre Erich Gamma avec lequel il crée JUnit qui, suite à sa popularité, entraînera la création de nombreux Frameworks de tests unitaires, cet ensemble se nomme xUnit[3].

Utilité[4][modifier | modifier le code]

On écrit un test pour confronter une réalisation à sa spécification. Le test définit un critère d’arrêt (état ou sorties à l’issue de l’exécution) et permet de statuer sur le succès ou sur l’échec d’une vérification. Grâce à la spécification, on est en mesure de faire correspondre un état d’entrée donné à un résultat ou à une sortie. Le test permet de vérifier que la relation d’entrée / sortie donnée par la spécification est bel et bien réalisée.

Trouver les erreurs rapidement[modifier | modifier le code]

La méthode XP préconise d'écrire les tests en même temps, ou même avant la fonction à tester (Test Driven Development). Ceci permet de définir précisément l'interface du module à développer. Les tests sont exécutés durant tout le développement, permettant de visualiser si le code fraîchement écrit correspond au besoin.

Sécurise la maintenance[modifier | modifier le code]

Lors d'une modification d'un programme les tests unitaires signalent les éventuelles régressions. En effet certains tests peuvent échouer suite à une modification, il faut donc soit réécrire le test pour le faire correspondre aux nouvelles attentes, soit corriger l'erreur se situant dans le code

Documente le code[modifier | modifier le code]

Les tests unitaires peuvent servir de complément à l'API, il est très utile de lire les tests pour comprendre comment s'utilise une méthode. De plus il est possible que la documentation ne soit plus à jour, mais les tests eux correspondent à la réalité de l'application.

Fonctionnement classique d'un test unitaire[modifier | modifier le code]

On définit généralement 4 phases dans l'exécution d'un test unitaire:

  1. Initialisation (fonction setUp): définition d'un environnement de test complètement reproductible (une fixture)
  2. Exercice: le module à tester est exécuté
  3. Vérification (utilisation de fonctions assert): comparaison des résultats obtenu avec un vecteur de résultat défini. Ces tests définissent le résultat du test: SUCCÈS (SUCCESS)ou ÉCHEC (FAILURE). On peut également définir d'autres résultats comme EVITE (SKIPPED).
  4. Désactivation (fonction tearDown): désinstallation des fixtures pour retrouver l'état initial du système, dans le but de na pas polluer les tests suivants. Tous les tests doivent être indépendant et reproductible unitairement (quand exécuté seul).

Utiliser les test unitaires[modifier | modifier le code]

Il s'agit pour le programmeur de tester un module, indépendamment du reste du programme, ceci afin de s'assurer qu'il répond aux spécifications fonctionnelles et qu'il fonctionne correctement en toutes circonstances. Cette vérification est considérée comme essentielle, en particulier dans les applications critiques. Elle s'accompagne couramment d'une vérification de la couverture de code (évaluation de la couverture structurelle), qui consiste à s'assurer que l'ensemble des tests conduit à exécuter l'ensemble (ou une fraction déterminée) des instructions présentes dans le code.

L'ensemble des tests unitaires doit être rejoué après une modification du code afin de vérifier qu'il n'y a pas de régressions (l'apparition de nouveaux dysfonctionnements). L’emploi d’une « stratégie de test » particulière peut limiter les tests à rejouer, par exemple : une analyse d’impact des modifications, corrélée à une preuve d’indépendance des modules, permet de cibler les cas de test unitaire à rejouer.

Commencer par les tests[modifier | modifier le code]

Un test doit correspondre aux spécifications de l’application, il faut donc écrire les tests en premier puis les faire passer par la suite plutôt que d'écrire le code avant et de prendre le risque d’être influencé par celui-ci lors de la rédaction des tests [5]. Bob Martin[6], grand défenseur de la méthode TDD propose un modèle simple pour l'écriture des tests unitaires :

  1. écrire une fonction de test qui doit obtenir un résultat défini dans les spécifications. Ce code appelant un code qui n'existe pas encore, celui-ci doit faillir. Ceci à pour but de définir une fonction qui teste "quelque chose".
  2. écrire le code (le "quelque chose") pour faire passer le test
  3. Une fois test en succès, rajouter un autre test pour obtenir un résultat légèrement différent, en faisant variant les entrées par exemple. Ce nouveau test fera faillir le code principal
  4. Modifier le code principal pour faire passer les tests
  5. recommencer, en éliminant et refactorisant les éventuels redondances dans le code des tests. On refactorise en même temps le code principal que le code des tests
  6. un test unitaire doit tester une caractéristique et une seul. On ne définit pas un "scénario" de test complexe dans un test unitaire.
  7. Il est déconseillé de tester les fonctions privées d'une classe, on se concentrera à tester les fonctions publique, c'est à dire, les interfaces avec lesquelles les acteurs extérieurs interagissent.

Utiliser des mocks[modifier | modifier le code]

Les mocks sont des objets permettant de simuler un objet réel de façon contrôlée ; dans certains cas, l'utilisation de mock est primordial, pour un gain de temps de couverture de code, et de fiabilité des tests[7] :

  • Pour simuler une base de données, un service, un web-serveur, etc., les interactions entre l'application et ces outils prennent du temps, l'utilisation de mock pour simuler leurs fonctionnements peut être un gain de temps considérable ;
  • Certains cas d’erreurs sont très difficile à reproduire, l'utilisation de mock permet ici de simuler une erreur pour pouvoir traiter ce cas et donc améliorer la couverture de code, par exemple le catch d'une exception ;
  • Sans l'utilisation de mock le test peut retourner une erreur ne provenant pas du code qui est testé (par exemple une base de donnée).

Cependant, une utilisation abusive de mock peut avoir l'effet inverse, notamment allonger le temps d'exécution des tests, rendre les tests compliqués à comprendre.

Génération de test unitaire[modifier | modifier le code]

La plupart des frameworks de la famille xUnit permettent la génération des classes de test unitaire. Cependant ces frameworks ne fournissent que le squelette des classes. Les tests devront donc être écrits par le développeur.

La génération de test unitaire un sujet important pour les chercheurs et plusieurs conférences s’intéressent à cette problématique tels que International Symposium on Software Testing and Analysis (ISSTA), International Conference on Software Engineering (ICSE) et Automated Software Engineering (ASE) .

Correction de test unitaire[modifier | modifier le code]

Lors d'une modification dans le code d'un programme, il se peut que certains tests ne passent plus ; dans ce cas, le développeur doit déterminer si cela vient du code en lui-même ou du test : si cela vient du test, le développeur doit modifier son test car la suppression de celui-ci entraînerait une augmentation des chances de régression du programme. Certains chercheurs ont développé des outils pour résoudre ce problème.

ReAssert[8] est un outil suggérant des réparations pour un test qui échoue, il analyse les tests a modifier et suggère des changements au développeur, si cette suggestion convient au développeur, il peut effectuer le changement en cliquant sur un bouton.

Test unitaire paramétrable[modifier | modifier le code]

Les tests unitaires paramétrables sont des tests qui prennent des paramètres. Ils peuvent ensuite utiliser des outils comme QuickCheck pour générer des paramètres. Ces tests sont supportés par JUnit, TestNG et Nunit.

En s'appuyant sur des cas concret d'entrée et sortie, la génération d'oracle, et sur la couverture de test pour minimiser les cas, des chercheurs ont réussi a générer des tests unitaires paramétrables[9]. Les résultats de cette méthode sont prometteurs.

Environnements de test[modifier | modifier le code]

Un environnement préparé (framework) pour réaliser facilement des tests unitaires existe dans les principaux langages de programmation. Par exemple Test::More [10] pour le Perl.

Frameworks xUnit[modifier | modifier le code]

Le terme générique « xUnit » désigne un outil permettant de réaliser des tests unitaires dans un langage donné (dont l'initiale remplace « x » le plus souvent).

Outils commerciaux[modifier | modifier le code]

Divers outils permettent l'automatisation des tests unitaires :

  • Le module TBrun de la suite d'outils LDRA ;
  • Tessy (distribution : Hitex Development Tools/Allemagne).

Articles connexes[modifier | modifier le code]

Sur les autres projets Wikimedia :

Notes[modifier | modifier le code]

  1. Camp Smalltalk SUnit
  2. Simple Smalltalk Testing:With Patterns, in Smalltalk Report octobre 1994, Kent Beck
  3. Xunit, Martin Fowler
  4. 10 Reasons to Write Unit Tests
  5. Unit Testing Best Practices, Roy Osherove.
  6. Test First, bob Martin.
  7. Robert Martin, When to Mock.
  8. (en) Brett Daniel, Danny Dig, Tihomir Gvero, Vilas Jagannath, Johnston Jiaa, Damion Mitchell, Jurand Nogiec, Shin Hwei Tan et Darko Marinov, « ReAssert: a tool for repairing broken unit tests », ICSE,‎ 2011 (lire en ligne).
  9. (en) Gordon Fraser et Andreas Zeller, « Generating Parameterized Unit Tests », ISSTA,‎ 2011 (lire en ligne).
  10. Test::More sur CPAN, entre autres modules de tests. Voir aussi les articles des mongueurs de Perl dans Linux Magazine France disponibles sur http://articles.mongueurs.net/magazines
  11. AUnit
  12. AsUnit
  13. CppUnit et en version 2
  14. CUnit
  15. GoogleTest
  16. Boost.Test
  17. HUnit
  18. JsUnit
  19. Welcome to The New JUnit.org! | JUnit.org
  20. http://testng.org/
  21. NUnit - Home
  22. NUnitAsp - ASP.NET unit testing
  23. OUnit
  24. PHPUnit - Trac
  25. SimpleTest - Unit Testing for PHP
  26. Atoum
  27. utPLSQL
  28. http://unitjs.com Unit testing for Javascript
  29. Simple Smalltalk Testing: With Patterns
  30. http://rpgunit.sourceforge.net/