Comme beaucoup de concepts en informatique, le concept de test unitaire n'est pas simple à définir. Non pas qu'il soit réellement complexe, mais plutôt parce que beaucoup de développeur se font leur propre idée de ce qu'est un test unitaire…
Définition Un test unitaire est un procédé permettant de s'assurer du bon fonctionnement d'une unité de programme.
OK, cette définition est assez générale, il ne prend pas de risque vous direz-vous peut-être. Certes, mais il est vrai que le terme « unitaire » peut lui-même renvoyer à ces réalités assez différentes. Une unité de programme est-elle une fonction ? Une classe ? Une instruction ?
Au delà des définitions, une bonne manière de comprendre le testing unitaire est d'en comprendre le but, de façon pragmatique.
Il s’agit simplement de vérifier, en fonction de certaines données fournies en entrée d’un module de code (on parle parfois d'unité fonctionnelle testée, ou SUT pour system under test), que les données qui en sortent ou les actions qui en découlent sont conformes aux spécifications du module.
En d'autres termes, il s'agit de vérifier le respect du contrat de service du module.
Considérons un système informatique composé de deux gros modules interdépendants. Dans chacun de ces modules, nous trouvons différents composants, eux-même dépendants entre eux.
Sur notre exemples, le module D
accepte en entrée des données qui lui sont fournies par le composant A
, et produit des données en résultat qui sont utilisées par le composant B
. OK ?
Imaginons maintenant que le développeur Arnaud soit responsable du développement et de la maintenance du composant A
, que Brigitte soit responsable du composant B
, et que Daniel soit responsable du composant D
.
Si Arnaud fait mal son boulot en déployant un composant A
buggué qui n'implémente pas correctement la spécification qui lui a été fournie, il y a fort à parier que le composant D
du pauvre Daniel plantera lamentablement, même s'il est correctement développé. C'est alors que commencent de longues et pénibles discussions, du genre « Non, mais c'est ton code qui est faux, non c'est le tien, bla, bla, bla… ».
Une solution simple pour s'assurer que chacun fait correctement son travail est tout simplement d'implémenter des tests unitaires sur chacun des composants. Cette tâche revient à chaque développeur : Arnaud écrira les tests unitaires de A
, Brigitte écrira les tests unitaires de B
, Daniel écrira les tests unitaires de D
, etc.
Dans le cas de Daniel, il s'agit de s'assurer que si le composant D
reçoit les données qu'il attend (i.e. conformes à la spec), alors il produit les résultats qu'il est censé produire (i.e. conformes à la spec). Ni plus, ni moins.
Les tests unitaires de D
auront donc grosso-modo cette forme :
En d'autres termes : si je fais entrer le jeu de données x1
dans la moulinette D
, alors il sort y1. Si je fais entrer le jeu de données x2
dans la moulinette D
, alors il sort y2
.
Il y a en fait peu de pré-requis pour pouvoir tester son code unitairement. Nous le verrons plus loin, quasiment tous les langages de programmation et tous les frameworks de développement dignes de ce nom proposent des outils de testing unitaire.
Le principal pré-requis au testing est la modularité du code. Une idée simple : diviser pour mieux régner. Un code modulaire est bien plus robuste qu'un imbroglio de règles de gestion et de traitements. Plus il est modulaire, plus un code est facile à tester unitairement…
Attendez ! Avant de coder, il faut comprendre les concepts. Et c'est généralement une mauvaise idée d'apprendre les concept en même temps que leur concrétisation. Nous verrons des exemples concrets dans les sections suivantes…
Qu'est-ce qu'un test unitaire ?
La mise en place d'une stratégie d'intégration continue repose généralement, en premier lieu, sur une solide politique de testing unitaire. Ainsi, il est possible de déclencher aisément (via des hooks) l'exécution des tests unitaires avant ou après un commit, avant ou après un déploiement, etc.
En quoi la notion de contrat de service permet-elle d'aider à l'écriture de tests unitaires ?
Concrètement, le contrat de service est généralement la documentation technique d'un composant (imaginez par exemple la JavaDoc d'une classe Java…).
Soient deux composants A et B, avec A qui alimente B en données. Le créateur de B accuse A de lui fournir des données pourries. Que doit faire le créateur de A ?
Pour les composants logiciels de type « boite noire », les tests unitaires sont la meilleure manière de démontrer que le composant réalise son travail correctement.