TestNG, nouveau framework de tests unitaires Java

Exemple simple de tests

Nous allons maintenant passer à un exemple permettant de mettre en oeuvre les principes de base des tests unitaires avec TestNG.

Principes généraux du test

Dans cet exemple nous allons utiliser deux objets métiers, à savoir :

Chaque apprenti est donc lié à une filière. Ceci se fait grâce à une référence d'un objet Stream stockée dans l'Apprentice.

Nous allons par la suite stocker des apprentis dans une HashMap afin de pouvoir stocker des éléments et les récupérer par la suite. Cette HashMap est gérée par une autre classe qui nous permettra de la remplir rapidement, de la vider, de récupérer un élément et d'en récupérer la taille. Ceci sera utile pour les méthodes de setUp(), tearDown() et pour les assertions.

Mise en oeuvre des tests : TestNGApprenti.java

Les méthodes de tests

Dans la classe correspondant réellement aux tests, nous allons utiliser des méthodes annotées par @Configuration afin de mettre en place les données nécessaires à nos test. Nous pouvons citer la création de la HashMap dans la classe ApprenticeData.java, son remplissage et son vidage à la fin de chaque test pour retourner à l'état initial.

Nous avons ensuite 6 méthodes de tests permettant chacune de vérifier les fonctionnements de nos objets :

Exemple de code de méthodes (autres méthodes disponibles dans les sources) :


/**
 * Vérifie les attributs de l'apprenti
 */
@Test(groups = "ir", enabled=true)
public void attributesTestingShouldBeOk(){
   Apprentice app = _appmap.getApprenti(_appmap.size()-1);
   
   /* Vérification que les attributs sont non nuls */	        
   assertNotNull(app.getName(), "Le nom ne devrait pas etre null");
   assertNotNull(app.getFirstName(), "Le prenom ne devrait pas etre null");
   assertNotNull(app.getStream(), "La filiere ne devrait pas etre null");

   /* Vérification que les valeurs des attributs sont similaires à ceux attendus */     
   assertEquals(app.getName(), _apprenti.getName(), "Les noms devraient etre identiques");
   assertEquals(app.getFirstName(), _apprenti.getFirstName(), "Les prenoms devraient etre identiques");
   assertEquals(app.getStream().getLibelle(), _apprenti.getStream().getLibelle(), "Les filieres devraient avoir le meme nom");
   assertEquals(app.getStream().getSchoolName(), _apprenti.getStream().getSchoolName(), "Les filieres devraient avoir la meme école d'attache");
}

/**
 * Méthode qui attend une Exception.
 */
@Test(groups="ir")
/* On attend une NullPointerException ou une Exception en héritant */
@ExpectedExceptions(NullPointerException.class)
public void methodeExceptionOK(){
   throw new NullPointerException();
}

Le fichier testng.xml associé


<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
  
<suite name="IR"  verbose="5" >
  <test name="Apprenti">
    <classes>
      <!-- Ajout de la classe de tests définie précédemment --> 
      <class name="base.TestNGApprenti"/>
    </classes>
  </test>
</suite>

Exécution des tests

Nous allons maintenant pouvoir exécuter le test grâce à notre fichier Ant. Nous allons pouvoir voir que les traces Ant nous renseigne énormément sur le déroulement de l'exécution.


# On voit les méthodes de tests trouvées par le moteur de TestNG. 
# Nos 6 méthodes sont présentes ainsi que les méthodes de "configuration". 
   [testng] TESTCLASS: base.TestNGApprenti
   [testng] [TestClass] BeforeClass : base.TestNGApprenti.setUpData()
   [testng] [TestClass] BeforeMethod:	base.TestNGApprenti.beforeMethode()
   [testng] [TestClass] Test        :		base.TestNGApprenti.attributesTestingShouldFail()
   [testng] [TestClass] Test        :		base.TestNGApprenti.methodeignore()
   [testng] [TestClass] Test        :		base.TestNGApprenti.methodeExceptionNonOK()
   [testng] [TestClass] Test        :		base.TestNGApprenti.attributesTestingShouldBeOk()
   [testng] [TestClass] Test        :		base.TestNGApprenti.methodeExceptionOK()
   [testng] [TestClass] Test        :		base.TestNGApprenti.managerGetApprenti()
   [testng] [TestClass] AfterMethod :	base.TestNGApprenti.afterMethode()
   [testng] [TestClass] AfterClass  : base.TestNGApprenti.tearDownData()
   [testng] [TestClass]
   [testng] ======

# Nos méthodes seront exécutées de manière parralèlle.
# On voit que la méthode ignorée a disparu de l'exécution.
   [testng] [TestRunner] PARALLEL LIST:
   [testng] [TestRunner]   base.TestNGApprenti.methodeExceptionNonOK()
   [testng] [TestRunner]   base.TestNGApprenti.managerGetApprenti()
   [testng] [TestRunner]   base.TestNGApprenti.attributesTestingShouldFail()
   [testng] [TestRunner]   base.TestNGApprenti.methodeExceptionOK()
   [testng] [TestRunner]   base.TestNGApprenti.attributesTestingShouldBeOk()
   [testng] [TestRunner] SEQUENTIAL LIST:
   [testng] [TestRunner] ===
   [testng] [TestRunner] Found 5 applicable methods

# Nous voyons ici l'ordre d'exécution des méthodes.
# Nous voyons bien les alternances entre tests et méthodes de "configuration".
   [testng] [Invoker 5383406] Invoking base.TestNGApprenti.setUpData()
   [testng] [Invoker 5383406] Invoking base.TestNGApprenti.beforeMethode()
   [testng] [Invoker 5383406] Invoking base.TestNGApprenti.methodeExceptionNonOK
   [testng] [Invoker 5383406] Invoking base.TestNGApprenti.afterMethode()
   [testng] [Invoker 5383406] Invoking base.TestNGApprenti.beforeMethode()
   [testng] [Invoker 5383406] Invoking base.TestNGApprenti.managerGetApprenti
   [testng] [Invoker 5383406] Invoking base.TestNGApprenti.afterMethode()

   ...

   [testng] ***********

# Nous avons ici le résumé de l'exécution et donc les résultats.
   [testng] PASSED: managerGetApprenti
   [testng] PASSED: methodeExceptionOK
   [testng] PASSED: attributesTestingShouldBeOk

# Nous avons ici une erreur que nous attendions.
# Nous avons par ailleurs un message donnant la cause de l'erreur.
   [testng] FAILED: base.TestNGApprenti.methodeExceptionNonOK()
   [testng] org.testng.TestException:
   [testng] Expected an exception in test method base.TestNGApprenti.methodeExceptionNonOK()
   [testng] at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:556)
   [testng] at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:89)
   [testng] at org.testng.TestRunner.privateRun(TestRunner.java:622)
   [testng] at org.testng.TestRunner.run(TestRunner.java:505)
   [testng] at org.testng.SuiteRunner.privateRun(SuiteRunner.java:200)
   [testng] at org.testng.SuiteRunner.run(SuiteRunner.java:126)
   [testng] at org.testng.TestNG.run(TestNG.java:294)
   [testng] at org.testng.TestNG.privateMain(TestNG.java:382)
   [testng] at org.testng.TestNG.main(TestNG.java:330)

# Second test devant échoué, avec un message explicite que nous avons spécifié.
   [testng] FAILED: base.TestNGApprenti.attributesTestingShouldFail()
   [testng] java.lang.AssertionError: La filiere ne devrait pas etre null expected:<true> but was:<false>
   [testng] at org.testng.Assert.fail(Assert.java:73)
   [testng] at org.testng.Assert.failNotEquals(Assert.java:345)
   [testng] at org.testng.Assert.assertTrue(Assert.java:28)
   [testng] at org.testng.Assert.assertNotNull(Assert.java:273)

   ...

# Résumé de l'exécution du Test Apprenti
# Et création des rapports de tests (demandé dans le fichier testng.xml).
   [testng] ===============================================
   [testng] Apprenti
   [testng] Tests run: 5, Failures: 2, Skips: 0
   [testng] ===============================================
   [testng] Creating E:\workspace\Xpose_TestNG\reports\testng\base\testng-failures.xml
   [testng] Creating E:\workspace\Xpose_TestNG\reports\testng\base\toc.html
   [testng] Creating E:\workspace\Xpose_TestNG\reports\testng\base\Apprenti.properties
   [testng] Creating E:\workspace\Xpose_TestNG\reports\testng\base\index.html
   [testng] Creating E:\workspace\Xpose_TestNG\reports\testng\base\main.html
   [testng] Creating E:\workspace\Xpose_TestNG\reports\testng\base\groups.html
   [testng] Creating E:\workspace\Xpose_TestNG\reports\testng\base\methods.html
   [testng] Creating E:\workspace\Xpose_TestNG\reports\testng\base\classes.html

# Résumé de l'exécution de la suite IR.
   [testng] ===============================================
   [testng] IR
   [testng] Total tests run: 5, Failures: 2, Skips: 0
   [testng] ===============================================

Nous avons pu voir le fonctionnement d'un test de bout en bout. Nous voyons que les tests apportent de bonnes informations sur le coeur de nos applications en permettant un debug efficace.

Les sources de ce test

Les sources du test sont disponibles ici.