« 2008-08 | Page d'accueil | 2008-10 »

18/09/2008

JUnit pour tester son code JAVA

'JUnit' est un utilitaire codé en JAVA pour tester du code JAVA. Chaque test constitue un test 'unitaire'.

Cette librairie permet de créer un test, ou une suite de test, qui sont exécutés de manière automatique.  Ainsi, les tests appliqués au code de l'application sont identiques à chaque fois. Le développeur n'a pas à exécuter une séquence de test, toujours dans le même ordre, manuellement. JUnit permet de maintenir la qualité du code dans le temps, même si des évolutions sont apportées. JUnit est un outil très apprécié dans la pratique de "l'extreme programming".

Les résultats de l'exécution des tests peuvent être affichés sous la forme textuelle, ou bien présentés par une interface graphique (SWING, ou AWT).

Pour utiliser JUnit, il faut commencer par placer l'archive 'junit.jar' dans le classpath de l'application.

  • Rédaction d'une classe de test, avec le 'framework JUnit'

La classe de test doit étendre la classe 'junit.framework.TestCase'. Les tests à effectuer sont regroupés dans les méthodes de cette classe. Pour que JUnit puisse localiser les méthodes de test automatiquement, il faut que leur nom commence par 'test', que la méthode soit 'public' et sans argument.

ex.:
import junit.framework.*;

public class monTest extends TestCase {
 
  public void testQQChose() {
    ...
  }
 
  public void testAutreChose() {
    ...
  }
}

Les comparaisons qui doivent être validées, se font avec les fonctions assertXXX() (cf doc. API JUnit).
Par exemple, pour vérifier l'égalité de deux chaines de caractères, on utilisera la fonction "assertEquals("message d'erreur", chaine1, chaine2)".

Plusieurs tests peuvent être codés dans la même méthode de test. Mais, il faut savoir que l'échec d'un test d'une méthode, entraine l'echec des autres tests de la méthode. Il faut donc faire attention à ne pas 'masquer' des résultats d'autres tests, suite à l'échec d'un test.

Les créations d'objets (fixtures) communes à plusieurs tests peuvent être initialisés et regroupés dans la méthode 'setUp()'.
La méthode 'setUp()' est exécutée avant chaque test (ex: ouvrir une connection). De même, une fonction 'tearDown()' est exécutée après chaque test. Ce qui est utile, pour libérer des ressources (ex: fermer une connection), par exemple.

  • Création d'une suite de tests

Les différentes méthodes de test peuvent ensuite être agencées en 'suite'. Une suite permet de définir un ordre, et des conditions d'exécution pour les tests.
ex:
public static Test suite() {
  TestSuite suite = new TestSuite();
  suite.addTest(new monTest("testQQChose"));
  suite.addTest(new monTest("testAutreChose"));
  return suite;
}

  • Lancer l'exécution d'une classe de test individuellement par programmation

Pour exécuter les test individuellement par programmation, il faut rajouter une méthode 'main' à la classe de test. ex:
public class TestEmployeeForm extends TestCase {
 ...
 public static void main(String[] args) {
  junit.textui.TestRunner.run(new TestSuite(TestEmployeeForm.class));
 }
}

Suivant que l'on souhaite visualiser les résultats en mode texte, avec une interface graphique 'SWING', ou avec une interface graphique 'AWT', on utilisera la méthode 'run' du package 'junit.textui.TestRunner', 'junit.swingui.TestRunner', ou 'junit.awtui.TestRunner'.

  • Execution des tests avec ANT

Il est possible de lancer l'exécution des tests depuis Ant. Il suffit pour cela de rajouter une cible (target) 'junit' correspondante. Celle-ci dépend de la compilation:
ex:
  <target name="junit" depends="compile">
    <junit showoutput="true" outputtoformatters="true" printsummary="yes" haltonerror="true" fork="false" >
      <classpath>
        <pathelement location="${classes.dir}" />
      </classpath>
      <formatter type="plain" usefile="false" />  
      <test name="yoz.form.TestEmployeeForm" fork="false" />
    </junit>
  </target>

A noter, qu'il faut inclure le répertoire contenant la classe de test, dans le 'classpath':
"<classpath><pathelement location="${classes.dir}" /></classpath>"
C'est la racine du répertoire qui doit être spécifiée, afin de conserver l'organisation des packages. Dans le cas contraire, une erreur est lancée par le classloader lors de l'exécution d'ant, car la classe de test reste introuvable.

La tâche Ant à effectuer est 'junit'. Cette tâche est accompagnée de paramètres. L'affichage des résultats des tests, se fait textuellement soit par la console, soit dans un fichier texte.


Références:
- 'Java Extreme programming Cookbook' aux éditions O'Reilly, par Mr Burke, et Mr Coyner
- 'JUnit Test Infected: Programmers Love Writing Tests', doc livrée avec l'archive 'junit'
 - 'Optional Tasks/JUnit', documentation livrée avec l'archive 'ant'

Auteur: Euan MATEO

03/09/2008

Data Access Object: un peu de souplesse dans un monde de brutes

Le Data Access Object (Abstract) Factory est ce que l'on appelle un 'Design Patern'.

'DAO Factory' fait le lien entre la couche métier et l'accès aux données d'une application.
Le but de cette technique est de rendre la couche métier indépendante de la manière dont
l'accès aux données se fait. Ainsi, il est plus facile de migrer d'un système de base de
données vers un autre, ou de changer la manière dont on veut accéder aux données.

La couche DAO permet d'instancier des objets d'accès aux données, pour chaque type
de données (utilisateur, commande, facture..). Concrètement, les méthodes proposées
par les objets d'accès aux données, pour la manipulation des données dans la couche métier,
ne dépendent pas du choix de stockage et d'accès aux données.

L'utilisation d'un shéma d'héritage pour la fabrique, fait que la couche métier
ne sait rien de la manière dont est fabriqué l'objet de connection.
Avec l'utilisation d'une interface pour les objets d'accès aux données, la couche métier
appelle les mêmes méthodes pour effectuer des opérations sur les données,
quelque soit la manière dont on accède aux données.


Le diagramme UML d'un DAO se présente ainsi:

DAOFactory.PNG

(Insérer schema)




+ Chronologie pour une nouvelle DAO +

1) On crée une fabrique abstraite DAOFactory qui pourra être étendue/sous-classée.
Ajout d'une méthode, et de constantes statiques pour proposer le choix de la fabrique
2) Une sous-classe de DAOFactory (=fabrique) est créee pour chaque type de base de données, ou de connection.
Chaque fabrique permet de créer tous les objets DAO nécessaires à la manipulation des données
métier (ex: employé, utilisateur, commande, facture..)
3) Création d'une interface pour chaque classe d'accès aux données, qui regroupe
les opérations/méthodes disponibles (ex: create, update, delete, edit, list..)
4) Création des classes d'accès aux données qui implémentent l'interface (cf 3)
5) Intégration de la DAO dans le code applicatif:
- Nouvelle fabrique
- Nouvel objet DAO
- Appel de méthode sur l'objet DAO


Références:
- http://java.sun.com/blueprints/corej2eepatterns/Patterns/... > Data Access Object
- "Design Patterns: Elements of Reusable Object-Oriented Software" écrit par Gamma, Helm, Johnson, Vlissides

Auteur: Euan MATEO