Comment tester un logiciel en tant que développeur, 2ème Partie
Par Thornton ROSE
Article publié sous le titre "Software
Testing for Programmers, Part
2",sur Gamelan.com
- Copyright
© 2002, Thornton Rose
« Whenever you are
tempted to type something into a print statement or a debugger expression, write
it as a test instead. » — Martin FOWLER
(Chaque fois que vous
êtes tenté d'écrire quelque chose sur un relevé imprimé
ou un message de débogage, rédigez le plutôt sous forme
de test.)
> Dans l'article sur le testing de logiciel — Comment
tester un logiciel en tant que développeur, 1ère Partie
— j'ai présenté les bases des tests qui permettent d'évaluer
un logiciel en tant que développeur. Dans cet article, je vais illustrer
le propos en vous montrant comment tester des applications Java à
l'aide de JUnit, structure de tests Open source conçue
pour l'évaluation des programmes Java.
JUnit
> Le coeur de JUnit est composé d'un ensemble de
classes et d'interfaces qui fournissent une trame facile à utiliser
pour construire des tests automatisés. Il est composé comme suit
:
- TestCase
Définition des tests et de leurs fixtures
- Test
Definition de l'interface pour exécuter un test
- TestSuite
Assemblage et réalisation d'un ensemble de tests
- Assert
Ensemble de méthodes d'assertions
- TestRunner
Effectue Tests et TestSuites
Les Tests
> Les tests sont définis dans les TestCases.
Pour créer un test vous devez :
- Créer une classe qui étend junit.framework.TestCase.
- Ecrire au moins une méthode qui commence par le mot « test. »
> Par exemple, supposons que vous soyez en train de développer
une application dont le but est de vous aider à brasser une
bière maison et que dans cette application, vous ayez une classe qui
modélise un lot de bières et calcule le taux d'alcool par volume
(TAV).
- Voici le code destiné au calcul du TAV
:
import java.util.*;
public final class Biere {
// ...
/**
* Calcul du taux d'alcool par volume.
* tav = (di - df) * 131.25
*/
public double tav() {
if (di < 1.0) {
throw new ArithmeticException("DI <
1.0");
}
if (df < 1.0) {
throw new ArithmeticException("DF <
1.0");
}
return (di - df) * 131.25;
}
}
- Voici un test qui évalue Biere.tav() :
import junit.framework.*;
public class tavTest extends TestCase {
/**
* Construction d'un test TAV.
*/
public testTav(String name) {
super(name);
}
/**
* Test tav().
*/
public void testTav() {
Biere biere = new Biere();
biere.setDensIni(1.042);
biere.setDensFin(1.010);
assertEquals(4.200000000000004, biere.tav(),
0.0);
}
}
Les assertions
> Les assertions sont utilisées pour contrôler le
résultat d'un test vérifiant que les conditions attendues sont
bien retrouvées. Si une assertion échoue, le test échoue.
Sinon le test est réussi.
Les méthodes d'assertion sont définies dans junit.framework.Assert
et sont héritées par TestCase. Afin d'implémenter
une assertion, vous devez appeler la méthode d'assertion qui peut s'appliquer
à la condition attendue. Afin d'apporter plus de détails, on peut
préciser le message à afficher en cas d'échec de l'assertion.
> Voici quelques exemples d'assertions :
// Assertion établissant qu'un nombre donné est égal
à une valeur attendue :
assertEquals(1, x);
// Assertion établissant qu'une condition est vraie :
assertTrue("TAV supérieur à 1.0", tav >
1.0);
// Assertion établissant qu'une condition est fausse :
assertFalse(tav > 1.0);
// Assertion établissant qu'un objet est nul :
assertNull("cet Objet est nul.", cetObjet);
// Assertion établissant qu'un objet n'est pas nul :
assertNotNull(cetObjet);
// Assertion établissant que deux variables font référence
au même objet :
assertSame(obj1, obj2);
// Assertion établissant que deux variables ne font pas référence
au
// même objet:
assertNotSame(obj1, obj2);
Les exceptions
> Les tests pour les exceptions attendues sont effectués
en capturant les exceptions et forçant à l'échec, en appelant
fail, si l'exception n'est pas rejetée. À titre d'exemple, voici
le test de cas testTav précédent agrémenté
de quelques tests d'exception :
import junit.framework.*;
public class TavExceptionTest extends TestCase {
// ...
/**
* Teste tav() avec DI incorrecte.
*/
public void testInvalidDensIni() {
try
{
Biere biere = new Biere();
biere.setDensIni(1.0);
double tab = biere.tav();
// Force à l'échec.
fail("ArithmeticException
non rejetée.");
} catch(ArithmeticException e) {
// succès
}
}
/**
* Teste tav() avec DF incorrecte.
*/
public void testInvalidDensFin() {
try
{
Biere biere = new Biere();
biere.setDensFin(1.0);
double tav = biere.tav();
// Force à l'échec.
fail("ArithmeticException
non rejetée.");
} catch(ArithmeticException e) {
// succès
}
}
}
Les test fixtures
> Que se passe-t-il lorsque plusieurs tests opèrent
sur le même objet ou sur le même ensemble d'objets ? Les tests
doivent être effectués en prenant en considération cet ensemble
répertorié d'objets. C'est cet ensemble d'objet qu'on appelle
test fixture.
> Les fixtures sont donc implémentées dans
les cas de tests et utilisées pour initialiser et détruire
les objets qui sont communs aux tests dans un cas de test. Afin d'implémenter
une fixture :
- Définissez des variables privées pour les items au sein de
la fixture.
- Passez outre setUp() pour initialiser les items de la fixture.
- Passez outre tearDown pour clore toutes les ressources, telles
que fichiers ou connexion à une base de donnée, qui sont ouvertes
dans setUp().
Avant d'exécuter un test, on appelle setUp(). Après
l'exécution du test, qu'il soit réussi ou qu'il ait échoué,
on appelle tearDown().Chaque test récupère une nouvelle
fixture, et ainsi un test ne peut pas en perturber un autre.
> Continuons avec l'exemple de notre bière maison
et supposons que vous ayez ajouté une méthode à la classe
Biere qui calcule l'amertume de votre bière dont voici le code :
public final class Biere {
// ...
/**
* Calcule l'amertume en Unités d'Amertume Brassage maison
* (UAB).
* uab = sum [1, n]: onces * alpha acide
*/
public double uab() {
double total = 0.0;
for (Iterator i = houblonListe.iterator();
i.hasNext(); ) {
Houblon houblon = (Houblon) i.next();
total += houblon.uab();
}
return total;
}
}
> Voici un cas de test qui implémente une fixture et deux
tests pour Biere.uab() :
import junit.framework.*;
public class UabTest extends TestCase {
// Declaration des items dans la fixture.
private Biere biere;
private Houblon houblon1;
private Houblon houblon2;
/**
* Construction d'un test UAB.
*/
public HoublonTest(String name) {
super(name);
}
/**
* Initialisation de la fixture.
*/
protected void setUp() {
biere = new Biere();
houblon1 = new Houblon(1.0, 1.0);
houblon2 = new Houblon(2.0, 2.0);
}
/**
* Teste uab()sans houblon.
*/
public void testZeroHoublon() {
assertEquals(0.0, biere.uab(), 0.0);
}
/**
* Teste uab() avec un houblon.
*/
public void testOneHops() {
biere.addHoublon(houblon1);
assertEquals(1.0, biere.uab(), 0.0);
}
/**
* Teste uab() de plusieurs houblons.
*/
public void testMultipleHoublon() {
biere.addHoublon(houblon1);
biere.addHoublon(houblon2);
assertEquals(5.0, biere.uab(), 0.0);
}
}
Suites de tests
> Une Suite de Tests est simplement constituée d'un ensemble
de tests. Afin de créer une suite de tests, il faut :
- Déclarer un méthode publique statique (public static)
appelée suite
qui retourne un Test.
- Dans la méthode suite , créer une TestSuite, puis
ajouter les uns à la suite des autres.
- Retourner la suite de tests.
> Voici un exemple de céation d'une suite de tests
qui effectuera tous les tests de cas de la classe Biere :
import junit.framework.*;
/**
* TousTests est utilisé pour créer une suite qui contient
* tous les tests.
*/
public class TousTests {
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTest(new TestSuite(TavTest.class));
suite.addTest(new TestSuite(UabTest.class));
return suite;
}
}
Runners
> JUnit est livré avec deux programmes
d'exécution de tests : l'un a un interface Swing
alors que l'autre effectue les tests à partir de la ligne de commande.
- Pour effectuer les tests à l'aide de Swing runner, démarrez
junit.textui.SwingRunner.
- Pour effectuer les tests à partir de la ligne de commande, tapez :
java junit.textui.TestRunner
[class]
Ressources
> Liens proposés par l'auteur
> Voici pêle-mêle quelques liens complémentaires.
À propos de l'auteur
Thornton Rose est développeur indépendant à Atlanta, Ga
(USA). Vous pouvez le joindre par courriel à l'adresse suivante : thornton.rose@mindspring.com.
Traduit de l'anglais par Lionel RAFFIN pour le site
www.smart–doc.org / Avril
2004
Texte original sur le site
de l'auteur