Les bases des tests unitaires

Plan

  1. Concepts de TestCase et de Test
  2. Syntaxe de EasyUnit
  3. Comprendre les résultats
  4. Exécution des tests
  5. Points importants à retenir

 

Concepts de TestCase et de Test

Il est à noter que les termes TestCase et Test proviennent directement de la syntaxe de EasyUnit et que c'est pour cette raison qu'ils ne sont pas traduits dans le guide.

Un TestCase représente l'ensemble des tests effectués sur une classe, un module, une fonction ou une méthode. Habituellement, un TestCase est utilisé pour une classe et ses méthodes sont testées grâce à plusieurs Tests. Cela est par contre laissé à la discrétion de l'utilisateur. EasyUnit permet de contrôler l'exécution des TestCase mais ne permet pas le contrôle des tests: on peut donc écrire une extension à EasyUnit qui n'exécute que les TestCases provenant d'une liste, mais l'utilisateur n'aura pas de contrôle sur les tests exécutés (tous les tests d'un TestCase exécuté seront exécutés).

Un Test représente une fonction dans laquelle on effectue une ou plusieurs assertions (tests de vérité) sur une partie du code. Un test appartient toujours à un TestCase. Si un TestCase est exécuté (ils le sont tous par défaut), tous ses Tests sont exécutés.

Par exemple, on pourrait avec un TestCase pour une classe Pile et un Test par méthode de cette classe.

Retour au haut de la page

 

Syntaxe de EasyUnit

EasyUnit ne limite pas le nombre de TestCase ou de Tests par TestCase. Il ne limite pas non plus le nombre de TestCase par fichier.

Pour écrire un Test, l'utilisateur doit utiliser la macro suivante contenue dans le fichier d'en-tête "easyunit/testharness.h":

TEST(NomTestCase, NomTest)
{
  // code du test
}


On voit tout d'abord que le nom du TestCase précède le nom du test: le TestCase est créé implicitement lorsque EasyUnit rencontre le premier Test d'un TestCase. Le code du test se retrouve à l'intérieur des deux accolades. Tous les Tests appartenant au même TestCase devront indiquer le même nom dans NomTestCase.

Il est très important que tous les Tests d'un TestCase se suivent dans un même fichier: si un Test est séparé par un Test d'un autre TestCase ou se retrouve dans un autre fichier, EasyUnit créera deux TestCases différents portant le même nom.

On retrouve généralement dans le code d'un Test l'utilisation des macros ASSERT_TRUE(condition) ou FAIL_M(message). La première macro permet d'effectuer un test sur une condition. Si cette condition est vraie, on continue l'exécution du test, si la condition est fausse, l'exécution du Test est suspendue et EasyUnit enregistre un échec pour ce test. La deuxième macro permet d'enregistrer un échec automatiquement dans le Test et de suspendre son exécution. Si le test est exécuté entièrement et qu'aucun échec n'a été rencontré, EasyUnit enregistre alors un succès pour ce test.

Voici un exemple d'utilisation de ces macros (seule la syntaxe est importante ici et non pas la sémantique des tests):

TEST(TestPile, Taille)
{
  Pile p;
  ASSERT_TRUE(p.size == 0);
  if (p.size < 0) {
    FAIL_M("Taille inadéquate.");
  }
}

Il faut noter que les versions futures de EasyUnit comprendront d'autres macros plus flexibles du type ASSERT_TRUE.

Il est important de noter qu'avec la version standard d'EasyUnit, il est important d'ajouter l'instruction "using namespace easyunit;" pour créer des Tests. De plus, dans la version standard, EasyUnit attrape les exceptions lancées durant l'exécution d'un Test. EasyUnit suspend alors l'exécution du Test et enregistre une erreur (différent d'un échec) pour ce Test.

Pour exécuter les tests, il suffit de faire appel à la méthode statique runAndPrint() de la classe TestRegistry. On retrouve généralement cette méthode dans la méthode main() de l'application:

#include "easyunit/testharness.h"

using namespace easyunit;

int main() {
  TestRegistry::runAndPrint();
}

L'exécution de cette méthode fera afficher dans la console (sortie standard) les résultats de l'exécution de tous les TestCases.  

Retour au haut de la page

 

Comprendre les résultats

Voici tout d'abord un exemple de l'affichage normal d'un résultat de EasyUnit:


SUMMARY

Test summary: FAIL
Number of test cases ran: 2
Test cases that succeeded: 1
Test cases with errors: 0
Test cases that failed: 1

DETAILS

Test case "Stack" SUCCEEDED with 0 error(s), 0 failure(s) and 2 success(es):
  Test "size" SUCCEEDED!
  Test "isEmpty" SUCCEEDED!

Test case "CustomQueue" FAILED with 0 error(s), 2 failure(s) and 0 success(es):
  Test "test1" FAILED :
    Failure: "q->size() == 0" line 26 in QueueTest.cpp
  Test "test2" FAILED :
    Failure: "Buffer overflow" line 31 in QueueTest.cpp

La ligne "Test summary" indique si les tests unitaires ont globalement été un succès ou un échec: si un seul Test échoue, vous retrouverez alors "FAIL" alors que si tous les Tests enregistrent un succès, vous retrouverez "SUCCESS".

On retrouve ensuite le nombre de TestCases qui a été exécuté. Ce nombre devrait correspondre au nombre de TestCases différents que vous avez déclaré. Si ce nombre semble supérieur, il est alors probable qu'EasyUnit a créé des TestCase différents portant le même nom, car les Tests n'étaient pas contigus (voir plus haut à ce sujet).

Un TestCase est qualifié de succès (succeeded) si tous ses Tests ont enregistré un succès. Un TestCase est qualifié d'échec (failed) si un seul de ses Tests enregistre un échec. Un TestCase a une erreur quand une exception a été levée dans au moins un de ses Tests.

Les détails des TestCases suivent le sommaire. On retrouve alors pour chaque TestCase le nombre de Tests comportant un échec, une erreur ou un succès.

Chaque Test est ensuite détaillé: on indique s'il a connu un succès ou un échec. Si le test a rencontré un échec, on indique la ligne et la condition de son échec (ou le message d'erreur dans le cas de l'utilisation de la macro "FAIL_M").

Retour au haut de la page

 

Exécution des tests

Les TestCases sont exécutés dans un ordre non déterminé: ils doivent donc être conçus de façon indépendante pour éviter les effets de bord. Il en est de même pour les Tests à l'intérieur d'un TestCase: ils sont exécutés de façon non déterminée et ne doivent pas être conçus en fonction d'un ordre précis d'exécution.

Points importants à retenir

  • Un TestCase comprend plusieurs Tests
  • Les Tests d'un même TestCase doivent se suivre et se trouver dans le même fichier
  • Si une condition est évaluée fausse avec ASSERT_TRUE, EasyUnit suspend l'exécution du Test et passe au Test suivant: c'est pour cette raison qu'on ne retrouve qu'un seul échec dans un Test, même s'il y a possiblement plusieurs assertions qui sont fausses.


 


© 2004 Barthélémy Dagenais
EasyUnit est distribué sous la license LGPL