Tester Java avec Jython et PyUnit (Fr)

Réaliser les tests unitaires d'applications Java en combinant l'utilisation de Jython, PyUnit et Apache Ant peut être une viable alternative à l'usage exclusif de JUnit.

Un minimum de connaissance du concept de tests unitaires, de langage de programmation Java , d'Apache Ant, de Jython ainsi que de JUnit, le populaire environnement de tests unitaires pour Java, facilitera la lecture des paragraphes suivants. Pour plus d'information concernant ces différents environnements de développement et de test, consultez la section Ressources à la fin de cet article.

JyUnit, le code accompagnant cet article, se résume à la réécriture en Jython de l'exemple de tests unitaires de JUnit (MoneyTest). JyUnit est aussi accompagné d'un fichier Ant, build.xml, facilitant l'automation et l'intégration de Jython et des tests unitaires.

Les composants logiciels utilisés pour cet article sont couramment utilisés dans le contexte de développement Java et sont disponibles à partir des ressources listées à la fin de ce document.

Cet article est aussi disponible en Anglais.

Préparation - Installation

Afin de pouvoir tester JyUnit, votre environnement devra comporter les composants suivants:

  • Java 2 SDK:
    • Téléchargez et installez le kit de development Java (version 1.4.2 o 1.5.0).
    • Créez la variable d'environnement JAVA_HOME, pointant vers le chemin d'installation du kit de développement Java. Ceci simplifie, entre autres, l'installation d'Apache Ant.
  • Apache Ant:
    • Apache Ant n'est nécessaire que pour exécuter les tests depuis Ant et pour montrer quelques possibilités d'intégration de Jython avec Ant. Si vous n'installez pas Ant, il est aussi possible de lancer les tests à partir de scripts, .bat sous Windows ou .sh sous UNIX/Linux.
    • Téléchargez et installez Apache Ant.
    • Ajoutez le chemin du sous-répertoire bin du répertoire d'installation de Ant à votre PATH (Exemple: C:\ant\bin, si Ant est installé sous C:\ant).
  • Jython:
    • Téléchargez et installez Jython (la version 2.1 est recommandée).
    • Créez la variable d'environnement JYTHON_HOME et assignez-lui le chemin de répertoire d'installation de Jython. Certains fichiers JyUnit utilisent cette variable d'environnement.
    • Dans le fichier registry, situé dans le répertoire d'installation de Jython, assignez à la propriété python.security.respectJavaAccessibility la valeur false. Consultez le paragraphe Accessibilité ci-après pour plus d'information.
  • JUnit:
    • Téléchargez et installez JUnit.
    • Si vous décompactez l'archive junit3.8.1.zip dans le répertoire C:*, vous obtenez un nouveau dossier C:\junit3.8.1. JUnit n'est pas normalement nécessaire pour bénéficier du concept décrit dans cet article, mais pour cette démonstration, le fichier de tests unitaires de JyUnit utilise précisément l'exemple fourni avec JUnit, Money*. Ceci permet de faire la comparaison entre Jython et Java pour réaliser des test unitaires.
  • JyUnit (code accompagnant cet article):
    • Décompactez l'archive jyunit02.zip ou jyunit02.tgz dans le répertoire d'installation de JUnit, de façon à ce que le nouveau dossier, jyunit soit au même niveau que le dossier junit. Ceci ne modifie en rien les fichiers de JUnit. Prenant l'exemple d'une installation de JUnit sous C:\junit3.8.1, vous obtenez un nouveau répertoire:C:\junit3.8.1\jyunit.

Architecture des fichiers

Description des Fichiers JyUnit

Nom de Fichier Description
build.xml Fichier de projet Ant
java_test.bat Batch Windows pour lancer les test unitaires Java (JUnit)
java_test.sh Script Shell pour lancer lest tests unitaires Java (JUnit)
jython_test.bat Batch Windows pour lancer les test unitaires Jython (PyUnit)
jython_test.sh Script Shell pour lancer lest tests unitaires Jython (PyUnit)
license.txt Licence accompagnant les fichiers JyUnit
MoneyTest.py Fichier Jython (conversion du fichier Java MoneyTest.java)
readme.txt Fichier Readme

Accessibilité de Java

Le fichier registry de Jython est situé à la racine du répertoire d'installation de Jython. Assigner la valeur false à la propriété python.security.respectJavaAccessibility dans le fichier registry, permet aux scripts Jython d'accéder aux classes et membres de classes non public de Java:

...
# Setting this to false will allow Jython to provide access to
# non-public fields, methods, and constructors of Java objects.
#python.security.respectJavaAccessibility = true
python.security.respectJavaAccessibility = false
...

Ceci n'est pas recommandé pour des logiciels destinés à la production, mais s'avère très avantageux pour les tests unitaires. Sans cette modification, on obtient l'erreur suivante durant le lancement des tests unitaires Jython, à chaque fois que le programme exécute la méthode setup:

Traceback (most recent call last):
File "MoneyTest.py", line 55, in setUp
self.fMB1 = MoneyBag.create(self.f12CHF, self.f7USD)
AttributeError: class 'junit.samples.money.MoneyBag' has no attribute 'create'

La classe junit.samples.money.MoneyBag n'est pas déclarée public, en conséquence, elle ne peut être accédée que par d'autres classes du même package, junit.samples.money dans l'exemple. Ce n'est pas dû au fait que les tests soient implémentés en Jython. Si la classe Java MoneyTest n'appartenait pas au même package que MoneyBag, on obtiendrait l'erreur suivante à la compilation de MoneyTest.java:

MoneyTest.java:9: junit.samples.money.MoneyBag is not public in junit.samples.money;
cannot be accessed from outside package
import junit.samples.money.MoneyBag;

Ceci montre un des avantages d'utiliser Jython pour tester des applications Java, tout en gardant le code de test totalement séparé du code destiné à la production. Bien sûr, avec un peu plus d'élaboration, il est possible d'obtenir un effet similaire avec Java, soit en utilisant l'API Java de Reflection, java.lang.reflect, en utilisant des composants additionnels pour JUnit comme JUnit-addons ou encore en faisant appartenir les classes de test au même package que les classes à tester (directive package au début du code Java).

Les Tests Unitaires

Une fois que votre environnement est préparé conformément aux instructions ci-dessus, vous pourrez:

  • Lancer les tests unitaires originaux de JUnit en utilisant java_test.bat ou java_test.sh.
  • Lancer les mêmes tests unitaires écrits avec Jython, en utilisant jython_test.bat ou Jython_test.sh. Le script lance les tests 2 fois: avec traces et sans trace.
  • Et finalement lancer à nouveau les tests unitaires ci-dessus (Jython), en utilisant Ant.

Pour plus de détails concernant Money et MoneyTest utilisés pour illustrer les exemples de tests unitaires, vous pouvez consulter l'article JUnit Test Infected: Programmers Love Writing Tests, disponible sure le site the JUnit.

Tests Unitaires Originaux (JUnit)

Les scripts java_test.bat et java_test.sh, permettent de lancer les tests unitaires fournis avec JUnit:

C:\junit3.8.1\jyunit>java_test
......................
Time: 0.03
OK (22 tests)

Tests Unitaires Jython (PyUnit)

Jython permet d'exécuter des tests unitaires indépendamment de JUnit, parce qu'il contient PyUnit, l'environnement de tests unitaires Python. Vous pouvez vérifier la présence du module unittest.py qui se trouve dans le sous-répertoire Lib du répertoire d'installation de Jython.

Les scripts jython_test.bat et jython_test.sh permettent de lancer les mêmes tests unitaires à partir de Jython (PyUnit):

C:\junit3.8.1\jyunit>jython_test
===================
Test without traces
===================
......................
----------------------------------------------------------------------
Ran 22 tests in 0.040s
OK
================
Test with traces
================
{[12 CHF][7 USD]} *2 == {[24 CHF][14 USD]} ... ok
{[12 CHF][7 USD]} negate == {[-12 CHF][-7 USD]} ... ok
testBagNotEquals (__main__.MoneyTest) ... ok
{[12 CHF][7 USD]} + [14 CHF] == {[26 CHF][7 USD]} ... ok
{[12 CHF][7 USD]} - {[14 CHF][21 USD] == {[-2 CHF][-14 USD]} ... ok
{[12 CHF][7 USD]} + {[14 CHF][21 USD]} == {[26 CHF][28 USD]} ... ok
testIsZero (__main__.MoneyTest) ... ok
[12 CHF] + [7 USD] == {[12 CHF][7 USD]} ... ok
testMoneyBagEquals (__main__.MoneyTest) ... ok
testMoneyBagHash (__main__.MoneyTest) ... ok
testMoneyEquals (__main__.MoneyTest) ... ok
testMoneyHash (__main__.MoneyTest) ... ok
{[12 CHF][7 USD]} - [12 CHF] == [7 USD] ... ok
{[12 CHF][7 USD]} - {[12 CHF][3 USD]} == [4 USD] ... ok
[12 CHF] - {[12 CHF][3 USD]} == [-3 USD] ... ok
testPrint (__main__.MoneyTest) ... ok
[12 CHF] + [14 CHF] == [26 CHF] ... ok
[14 CHF] + {[12 CHF][7 USD]} == {[26 CHF][7 USD]} ... ok
[14 CHF] *2 == [28 CHF] ... ok
[14 CHF] negate == [-14 CHF] ... ok
[14 CHF] - [12 CHF] == [2 CHF] ... ok
testSimplify (__main__.MoneyTest) ... ok
----------------------------------------------------------------------
Ran 22 tests in 0.090s
OK

Les tests Jython sans trace affichent la même information que celle de JUnit et sont lancés avec la commande:

set CP=.;..;C:\jython\jython.jar
java -cp %CP% org.python.util.jython MoneyTest.py

Note: Le CLASSPATH représenté par .;..;C:\jython\jython.jar est un exemple. Ceci assume que Jython soit installé sous Windows dans le répertoire C:\jython et que l'archive JyUnit ait été décompactée conformément aux instructions de l'article, de façon à ce que junit\samples\money soit accessible dans le CLASSPATH avec "..".

Les tests avec traces sont obtenus en lançant la commande:

set CP=.;..;C:\jython\jython.jar
java -cp %CP% org.python.util.jython MoneyTest.py -v

Dans ce cas, pour chacun des tests, le commentaire de méthode, connu sous le nom de docstring dans le jargon Python, sera affiché. Par exemple, pour le test testBagSubtract:

def testBagSubtract(self):
  """{[12 CHF][7 USD]} - {[14 CHF][21 USD] == {[-2 CHF][-14 USD]}"""
  expected = MoneyBag.create(Money(-2, "CHF"), Money(-14, "USD"))
  self.assertEquals(expected, self.fMB1.subtract(self.fMB2))

PyUnit affiche:

{[12 CHF][7 USD]} - {[14 CHF][21 USD] == {[-2 CHF][-14 USD]} ... ok

S'il n'y a pas de documentation de méthode, PyUnit affiche le nom de la méthode, sa classe d'appartenance et le résultat:

testBagNotEquals (__main__.MoneyTest) ... ok

La méthode testBagNotEquals est implémentée dans le fichier MoneyTest.py comme suit:

def testBagNotEquals(self):
  bag = MoneyBag.create(self.f12CHF, self.f7USD)
  self.assertFalse(bag.equals(Money(12, "DEM").add(self.f7USD)))

La présence ou non des commentaires de méthode est simplement dictée par l'intention de rester au plus près de l'implémentation des tests en Java (MoneyTest.java). Les commentaires Java des méthodes de test on été traduits sous la forme de document de méthodes dans le fichier Jython (MoneyTest.py). Les méthodes sans commentaire se reconnaissent par l'absence de docstring dans MoneyTest.py.

Tester avec Ant

Les tests unitaires pilotés par le fichier build.xml ne comportent que les tests Jython. Les tests sont similaires à ceux exécutés directement par les fichiers de script. La particularité intéressante du fichier build.xml est de démontrer deux méthodes permettant de simplement intégrer Jython dans un fichier de construction Ant.

Pour lancer les tests, et assumant que les exécutables de Ant sont dans le PATH, entrez la commande suivante:

C:\junit3.8.1\jyunit>ant
Buildfile: build.xml
init:
test.call:
jython.call:
[java] ......................
[java] ------------------------------------------------------------
[java] Ran 22 tests in 0.060s
[java] OK
...

Les tests sont exécutés quatre fois:

  1. Sans trace via une tache AntCall.
  2. Avec trace via une tache AntCall.
  3. Sans trace via une tache MacroDef.
  4. Avec trace via une tache MacroDef.

AntCall est une tâche Ant appartenant aux tâches principales de Ant.

La version utilisant le mécanisme de définition de macro MacroDef est plus élégante à chaque appel de la tâche, mais MacroDef n'est disponible que depuis la version 1.6 de Ant.

Consultez le fichier build.xml fourni avec les sources de JyUnit pour voir des exemples concrets d'usage de AntCall et de MacroDef.

Conclusion

La vague Extrême Programming de la fin des années 90 s'est quelque peu affaiblie. Toutefois, certains éléments composant cette méthodologie sont restés partie intégrante de beaucoup de procédés de développement actuels. Les tests unitaires, en particulier, ayant démontré leur indiscutable valeur, ont même vu leur apparition au sein des environnements modernes de développement (Visual Studio, NetBeans, Eclipse...). La plupart des langages de développement sont accompagnés de leur librairie de tests unitaires et certains langages ont la notion de tests unitaires intégré à leur syntaxe (Exemple: D Programming Language). Pour pallier à la particularité de certains environnements, les librairies de base existantes ont été complétées par des librairies spécifiques: HTTPUnit et XMLUnit par exemple.

Où se situent Jython et PyUnit pour tester des applications Java dans cette large variété d'outils disponibles pour les tests unitaires? PyUnit utilisé avec Jython ne comporte pas d'interface utilisateur graphique (GUI). Il n'existe pas de tâche spécifique pour Ant. L'intégration dans les environnements de développement est par défaut basée sur JUnit. Jython et PyUnit peuvent donc apparaître plus limités que JUnit pour les tests unitaires d'applications Java.

Mais, en dépit de ces inconvénients apparents, PyUnit et Jython présentent certains avantages non négligeables:

  • Simplicité: Jython et PyUnit sont en accord avec l'adage Keep It Simple Stupid (KISS). En dépit de l'indéniable valeur des tests unitaires, il n'en demeure pas moins que la simplicité des tests unitaires devrait être la règle afin que la concentration de l'effort de développement soit dirigée vers le produit final plutôt que vers les tests unitaires: est-ce que l'utilisateur a besoin des tests unitaires?
  • Rapidité de développement: A la condition que le développeur soit familier ou prêt à se familiariser avec le langage Python (Le but de cet article n'est pas d'entrer en guerre de religion pour ou contre un langage de programmation particulier). La simplicité, la clarté et l'absence de compilation en font un outil particulièrement seyant pour les tests unitaires.
  • PyUnit est intégré dans Jython: il n'est pas nécessaire d'installer de produits autres que Jython.
  • Indépendance de code: Comme démontré dans les exemples, il est très facile de conserver une total indépendance entre les tests et le code Java à tester.
  • Facilité d'intégration: Bien que non intégré par défaut dans les environnements de développement populaires, il peut être aisé d'inclure Jython dans Ant et ainsi de piloter des tests unitaires. En ce qui concerne les environnements de développement intégré (IDE), les projets Coyote et JyDT devraient, à court terme, satisfaire ces conditions pour les produits respectifs NetBeans et Eclipse.

Certaines des caractéristiques listées ci-dessus ne sont pas forcément limitées à Jython et PyUnit. Il y a d'autres candidats pour tester Java. Les choix sont variés et peuvent consister en différentes associations: Jython et JUnit, Groovy et JUnit, BeanShell et JUnit... Ce ne sont que quelques exemples et chacune de ces combinaisons pourrait faire l'objet d'un nouvel article.

Téléchargement

Ressources

Mentions légales

Go Top
comments powered by Disqus