TestNG, nouveau framework de tests unitaires Java
Exemple de généralisation des tests, @Factory
Dans cet exemple aussi il faut bien comprendre le fonctionnement de cette Factory. Elle créé des instances d'une classe de tests et les sauvegardes. Lors du lancement, elle les exécutera. La classe exécutée peut, bien entendu, comporter autant de tests tests que voulu.
Principes généraux du test
Cet exemple utilise les mêmes éléments que celui concernant le passage de paramètres. On utilise la même base de données avec les mêmes informations stockées. Nous effectuerons le même test que pour les paramètres mais en utilisant une classe de test externe au lieu d'une méthode et d'un DataProvider.
Nous allons maintenant voir comment mettre cette technique en pratique.
Mise en oeuvre des tests : TestNGFactory.java et TestApprentice.java
Les méthodes de tests
La Factory : TestNGFactory
Dans la classe correspondant à la Factory, nous avons deux méthodes
- Object[] createInstances(String url, String user, String password)
Méthode qui sera annotée par @Factory. Elle permettra de récupérer les informations de la base de données et de créer des instances de la classe TestApprentice. Ces instances seront celles exécutées ensuite. - tearDownData() qui ferme juste la connexion JDBC à la base de données.
Voici les éléments principaux du code de la méthode annotée par @Factory :
/**
* Définition de la Factory
* On créé un tableau d'instances de la classe de test avec l'objet à tester
* Les invocations de cette classe seront faites automatiquement grâce à la FACTORY
*
* Passage de paramètres de la base
* @param url url de la base
* @param user le login de la base
* @param password le password de la base
* @return un tableau d'instances des tests à lancer
*/
@Factory
@Parameters({ "url", "user", "password" })
public Object[] createInstances(String url, String user, String password){
...
/* Tableau contenant nos instances. */
Object[] results = null;
...
/* Récupération des apprentis */
...
/* Instanciation et ajout dans notre tableau stockant les instances. */
/* Ici on stocke un apprenti avec filière. */
results[i] = new TestApprentice(new Apprentice(tab[0], tab[1], new Stream(tab[2], streamSchool)));
}
else{
/* Instanciation et ajout dans notre tableau stockant les instances. */
/* Ici on stocke un apprenti sans filière. */
results[i] = new TestApprentice(new Apprentice(tab[0], tab[1], null));
}
...
return results;
}
La classe de tests : TestApprentice
Pour la classe que la Factory instancie et qui effectuera réellement les tests, nous avons :
- TestApprentice(Apprentice apprentice)() qui est le constructeur et permet l'instanciation par la Factory de la classe.
- verifyApprentice() qui effectuera les tests sur l'apprenti récupéré à la construction de la classe.
Code des deux méthodes :
/**
* Constructeur de la classe de test
* @param apprentice
*/
public TestApprentice(Apprentice apprentice){
this.apprentice = apprentice;
}
/**
* Test les paramètres de l'apprenti qui est passé à la création de la classe de test via la Factory
*/
@Test(groups = "factory")
public void verifyApprentice(){
/* Vérification des attributs de l'apprenti en variable de classe. */
assertNotNull(apprentice.getName(), "Name should not be null");
assertNotNull(apprentice.getFirstName(), "First name of <"+apprentice.getName()+"> should not be null");
assertNotNull(apprentice.getStream(), "Stream of <"+apprentice.getName()+"> should not be null");
}
Le fichier testng.xml associé (similaire à celui du passage de paramètres) :
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Factory" verbose="5" >
<test name="Factory">
<!-- Définition des paramètres et de leur valeurs -->
<parameter name="url" value="jdbc:mysql://localhost:3306/Xpose"/>
<parameter name="user" value="root"/>
<parameter name="password" value=""/>
<classes>
<class name="parameters.TestNGFactory"/>
</classes>
</test>
</suite>
Exécution des tests
Le lancement s'effectue toujours de la même manière et va nous permettre de voir des éléments particuliers à la Factory. En effet, même sion exécute la Factory, c'est la classe instanciée que l'on retrouvera dans les logs. C'est logique, la Factory n'est présente que pour instancier la classe de test de manière dynamique et de les lancers par la suite.
# On voit ici que l'on exécute la classe de la Factory.
[testng] TESTCLASS: factory.TestNGFactory
[testng] [TestClass]
[testng] ======
[testng] [TestClass]
# Découverte de la classe que la Factory instancie et de sa méthode de test.
[testng] ======
[testng] TESTCLASS: factory.TestApprentice
[testng] [TestClass] Test : factory.TestApprentice.verifyApprentice()
[testng] [TestClass]
[testng] ======
# Exécution de cette méthode de test autant de fois qu'instanciée par la Factory (4).
[testng] [Invoker 41771] Invoking factory.TestApprentice.verifyApprentice
[testng] [Invoker 41771] Invoking factory.TestApprentice.verifyApprentice
[testng] [Invoker 41771] Invoking factory.TestApprentice.verifyApprentice
[testng] [Invoker 41771] Invoking factory.TestApprentice.verifyApprentice
[testng] *********** INVOKED METHODS
[testng] factory.TestApprentice.verifyApprentice() 9004539
[testng] factory.TestApprentice.verifyApprentice() 8814217
[testng] factory.TestApprentice.verifyApprentice() 31289430
[testng] factory.TestApprentice.verifyApprentice() 16399041
# Résumé, on voit les 4 lancements de la méthode de la classe de tests.
[testng] ***********
[testng] PASSED: verifyApprentice
[testng] PASSED: verifyApprentice
[testng] PASSED: verifyApprentice
# Erreur car l'apprenti n'a pas de filière.
[testng] FAILED: factory.TestApprentice.verifyApprentice()
[testng] java.lang.AssertionError: Stream of <Apprenti2> should not be null expected:<true> but was:<false>
...
# Résumé de l'exécution et création des rapports.
[testng] ===============================================
[testng] Factory
[testng] Tests run: 16, Failures: 4, Skips: 0
[testng] ===============================================
[testng] [Invoker 41771] Invoking factory.TestNGFactory.tearDownData()
[testng] Creating E:\workspace\Xpose_TestNG\reports\testng\factory\testng-failures.xml
[testng] Creating E:\workspace\Xpose_TestNG\reports\testng\factory\toc.html
[testng] Creating E:\workspace\Xpose_TestNG\reports\testng\factory\Factory.properties
[testng] Creating E:\workspace\Xpose_TestNG\reports\testng\factory\index.html
[testng] Creating E:\workspace\Xpose_TestNG\reports\testng\factory\main.html
[testng] Creating E:\workspace\Xpose_TestNG\reports\testng\factory\groups.html
[testng] Creating E:\workspace\Xpose_TestNG\reports\testng\factory\methods.html
[testng] Creating E:\workspace\Xpose_TestNG\reports\testng\factory\classes.html
[testng] ===============================================
[testng] Factory
[testng] Total tests run: 4, Failures: 1, Skips: 0
[testng] ===============================================
Nous avons ainsi vu que la Factory permettait d'instancier dynamiquement une classe de tests.
Les méthodes réellement exécutées n'ont au final aucun lien avec la Factory qui ne fait que son travail de création d'instances.
Les sources de ce test
Les sources du test sont disponibles ici.