Introduction au test logiciel/Intégration dans le processus de développement

Jusqu'ici, nous avons vu comment vous pouviez développer dans votre coin des tests portant sur le code que vous écrivez. Cependant, le développement d'un logiciel est un processus plus global : voyons comment les tests s'intègre avec les autres tâches à effectuer et comment on peut travailler en équipe.

Développer avec les tests

modifier

Bien que ce ne soit pas systématique, la pratique courante est que le code et les tests sont écrits par la même personne.

Écrire des tests prend du temps qu'on pourrait passer à écrire du code apportant de nouvelles fonctionnalités. Cela peut paraître frustrant, mais il faut renoncer à la course à la fonctionnalité pour préférer avancer plus lentement mais plus sûrement. En particulier, on est tenté de négliger l'écriture des tests après plusieurs semaines passées sur un projet, surtout en phase de bouclage où « on a plus le temps pour ça ». Appliquer une démarche de tests rigoureuse tout au long du projet permet de modifier le code aussi sereinement en fin de projet qu'au début (les tests assurant la non-régression). La solution réside dans l'équilibre, c'est en fonction des contraintes du projet et du sentiment de maîtrise des développeurs qu'il convient de placer le curseur entre deux extrêmes qu'il faut éviter : « pas de temps pour les tests » et « 100 % de couverture, peu importe le temps que ça prendra ! ».

Après plusieurs mois de pratique, un développeur peut se demander « À quoi bon écrire des tests, on ne trouve jamais de bogues ». On peut le comprendre étant donné qu'après la mise en place d'une démarche des tests, les bogues se raréfient : on peut alors avoir l'impression que les tests ne servent à rien. Ce serait se tromper. Pour s'en convaincre, il suffit d'abandonner les tests pour voir ressurgir des bogues à retardement.

Les tests et la gestion de version

modifier

Si vous travaillez à plusieurs sur le développement du projet, vous utilisez probablement un outil de gestion de version de code-source (comme CVS, Git ou Subversion). Vous pouvez l'utiliser pour stocker vos classes de tests aux côtés des classes.

Vous pouvez adopter des règles strictes :

  • Ne jamais envoyer une classe qui n'est pas accompagnée d'un test
  • Toujours faire passer les tests lorsqu'on récupère un projet
  • Avant de faire un commit :
    1. Relancer tous les tests
    2. Mettre à jour le code
    3. Repasser tous les tests
    4. Faire le commit

Le développement piloté par les tests

modifier

Le développement piloté par les tests (ou « Test-Driven Development » ou « TDD ») est une pratique souvent utilisée dans les méthodes agiles (on trouve son origine dans l'extreme programming). Elle consiste à développer l'application selon le cycle suivant :

  1. Écrire les tests
  2. Vérifier que ceux-ci ne passent pas
  3. Écrire le code manquant
  4. Vérifier que le test passe
  5. Remanier le code

1. Écrire les tests

modifier

Dans le développement piloté par les tests, les tests sont écrit avant le code. Il faut donc commencer par écrire un test ou un petit ensemble de tests qui représente les nouvelles fonctionnalités qu'on va implémenter durant le cycle.

Il faut pour cela se baser sur la spécification, les cas d'utilisation ou les user stories. Écrire les tests d'abord permet au développeur de voir vraiment le comportement qui est attendu avant de toucher au code.

2. Vérifier que les nouveaux tests échouent

modifier

Il est important de lancer les tests pour s'assurer que les autres passent toujours et que les nouveaux tests ne passe pas. Dans le cas contraire, deux possibilités :

  • Le test n'est pas bon
  • La fonctionnalité est déjà implémentée. Cela peut arriver étant donné qu'un développeur zélé peut, lorsqu'il touche une partie du code, ajouter dans la foulée un petit peu de plus de fonctionnalité que prévu.

Toutefois, si ce dernier cas se présente plusieurs fois, il faut se poser des questions sur la gestion du projet. Les tâches ont-elles bien été réparties ?

3. Écrire le code

modifier

Il est important d'essayer de n'écrire strictement que le code nécessaire au passage du test, pas plus. Vous pouvez relancer le test écrit à l'étape 1 autant de fois que nécessaire. Peu importe si le code n'est pas élégant pour l'instant tant qu'il permet de passer le test.

4. Vérifier que tous les tests passent

modifier

C'est le moment de vérifier que tous les tests passent, ceci afin de vérifier qu'avec les modifications faites on n'a pas créé de régressions dans le code.

5. Remanier le code

modifier

Enfin, refactorisez le code pour améliorer la conception tout en vérifiant que les tests passent toujours.

Intégrer les tests à la construction automatique du projet

modifier

Avec Ant

modifier

Si vous utilisez Ant pour construire vos projets, il existe la tâche junit. Attention, elle dépend des fichiers junit.jar et ant-junit.jar qu'il faut télécharger et indiquer dans le fichier build.xml. Référez vous à la documentation de la tâche junit dans la documentation officielle de Ant.

Avec Maven

modifier

Si vous utilisez maven pour construire et gérer les dépendances de votre projet. Vous pouvez lui demander de faire passer tous les tests.

mvn test

Maven intègre une convention pour placer les tests dans l'arborescence du projet. Vous pouvez déclarer JUnit et vos autres outils comme des dépendances. Vous pouvez également intégrer à la génération du site, la génération et la publication des rapports sur le respect de conventions et la couverture.

Gardez l'essentiel des tests rapidement exécutables

modifier

Quoi qu'il en soit, la construction du projet doit rester légère, et dérouler les tests ne doit pas prendre plus de quelques minutes. Le risque, en laissant le temps de construction s'allonger indéfiniment au fur et à mesure que les tests s'accumulent, est de lasser les développeurs qui seront tentés de sauter la phase de test de la construction.

Une bonne pratique consiste à laisser les tests unitaires dans la construction du projet et à ignorer les tests d'intégration dans le build par défaut. Une petite option (un profil Maven) permettant d'activer le passage des tests d'intégration. Le serveur d'intégration, lui, pourra construire systématiquement en repassant tous les tests.

Profitez des ressources du serveur d'intégration continue

modifier
 
Sonar donne une vue d'ensemble de l'évolution des métriques d'un projet

Si vous faites de l'intégration continue, vous utilisez un système de gestion de version et vous avez sûrement un serveur chargé de construire régulièrement le projet. Vous pouvez faire en sorte que chaque construction rejoue l'ensemble des tests pour vérifier que tout va bien. Cette construction automatique régulière peut donner lieu à la génération de rapports. Votre serveur d'intégration peut donc vous permettre de suivre l'évolution des métriques et de la complexité du projet.

Coupler le serveur d'intégration continu avec un outils de panneau de contrôle tel que Sonar vous permettra de suivre l'évolution du succès des tests et de la couverture du code en fonction du temps et de l'évolution de la complexité de l'application. Sonar permet de voir où se trouvent les portions de codes les moins testées.

Rapprocher les tests de l'utilisateur

modifier

Si les méthodes agiles recommandent les tests, elle recommandent également d'intégrer l'utilisateur au processus de développement afin que les réalisations des développeurs collent au plus près des véritables attentes qu'on peut avoir du logiciel.

Les tests sont d'abord une discipline technique mais nous allons voir que par différentes façon, nous pouvons rapprocher ces travaux des utilisateurs. Les trois approches suivantes tentent de mêler les tests avec la documentation utilisateur (le cas doctest) ou avec les spécifications du logiciel (Fit et BDD). L'objectif est d'avoir des tests écrit dans un langage compréhensible par l'utilisateur, voire de permettre à l'utilisateur d'écrire les tests lui-même sous forme de tableau de valeurs dans un tableur (Fit) ou en langage simili-naturel (BDD). Idéalement, ces technologies permettrait de confronter l'implémentation du logiciel aux spécifications attendues, décrites en langue naturelle : on peut parler de spécification exécutable (anglais executable spec).

Mêler tests et documentation, l'approche doctest

modifier

doctest est un outil livré avec Python qui permet, dans une documentation au format texte brut ou rST, d'ajouter des lignes qui permettent de vérifier ce qui vient d'être dit. L'exemple suivant montre un fichier texte d'exemple, il pourrait s'agir d'un fichier README.txt.

======================
Demonstration doctests
======================

This is just an example of what a README text looks like that can be used with
the doctest.DocFileSuite() function from Python's doctest module.

Normally, the README file would explain the API of the module, like this:

   >>> a = 1
   >>> b = 2
   >>> a + b
   3

Notice, that we just demonstrated how to add two numbers in Python, and 
what the result will look like.

En lisant ce fichier, doctest va interpréter les lignes préfixées par >>> et vérifier que ce qui est retourné par l'évaluation de l'expression correspond à ce qui est écrit (ici, doctest va vérifier que l'évaluation de a + b renvoie bien 3).

Des tests orientés données, l'approche Fit

modifier

Ward Cunningham propose dans son outil « fit » de rédiger les tests de validation dans un document, sans code. Son outil comprend un lanceur de test mais aussi un wiki embarqué, accessible via une interface Web. Dans ce wiki, les utilisateurs peuvent entrer, dans des tableaux, les jeux de données à fournir en entrée ainsi que les résultats attendus en sortie. Une autre possibilité, donnée aux utilisateurs, est d'entrer les jeux de données dans un tableur.

Java
Fit, FitNesse (avec trinidad), Concordion
PHP
phpfit

Des tests fonctionnels en phase avec les attentes de l'utilisateur, l'approche BDD

modifier

C'est en essayant d'enseigner l'approche TDD que Dan North s'est rendu compte que la plupart des développeurs acceptent volontiers d'écrire les tests avant le code mais se contentent de percevoir cette technique comme une façon de tester en même temps qu'on développe, sans percevoir ce qu'il s'agit en fait d'une méthode de développement. Pour faciliter cette compréhension de TDD, Dan North a étendu les notions de bases pour former Behavior Driven Development, une méthode sensée apporter aux développeurs tous les bénéfices d'une véritable approche de TDD.

Des tests bien nommés

modifier

Notion de « Comportement »

modifier

Les attentes utilisateurs sont aussi des comportements

modifier

Frameworks de BDD

modifier
Groovy
GSpec, Spock, easyb
Java
Cucumber-JVM, un port officiel de Cucumber pour les différents langages de la JVM, Instinct, JBee, JBehave, JDave, Robot Framework, Narrative
JavaScript
Jasmine
PHP
behat
Python
Freshen, Lettuce, Pyccuracy, Pyhistorian, PyCukes
Ruby
RSpec, Cucumber
Scala
ScalaTest, specs

Pour aller plus loin

modifier

Tests de non-régression

modifier

Il s'agit de tests qui visent à assurer la non-régression d'une application. C'est à dire qu'une modification apportée à l'application ne rend pas erronés les comportements d'une application qui fonctionnaient avant la modification.

« Beta test »

modifier

Le beta-test consiste à fournir une version quasi-finale de l'application (dite « version Beta ») à un large échantillon d'utilisateurs finaux. Ceci afin qu'ils rapportent les derniers bogues résiduels ou dans le cas d'une interface graphique, quelques améliorations ergonomique. La version livrée est estampillée « Beta » afin que les utilisateurs soient conscient qu'il ne s'agit pas le version finale et que le version présentée peut encore contenir des erreurs.

On parle de bêta ouverte ou de bêta fermée selon que cette version du logicielle soit accessible à tous les utilisateurs potentiels ou seulement à un groupe restreint.