Transcript Tests
POO
Tests Unitaires
Frédéric Moal
année 2010/2011
Différents type de tests
Vérification d'un système, logiciel ou matériel,
en vue de savoir s'il satisfait les exigences
requises en termes de qualité.
Qualité du code :
Découvrir les bugs avant qu'ils ne cassent l'application.
Assurer la qualité très tôt pendant le codage en utilisant des outils
d'analyse dynamique (débuggeur, génération du diagramme de séquence
pendant l'exécution, etc.)
2
Différents type de tests
Fonctionnalités :
réaliser des tests unitaires,
des tests de non-régression,
des tests d'intégration,
des tests de validation des composants dans l'application;
Performances :
Test de charge
mise à l'épreuve d’une application en émulant des “vrais” utilisateurs avec un outil
de génération de Charge
détecter les goulots d'étranglement sur les ressources systèmes à observer (temps
de réponses, occupation mémoire, etc.)
Réception :
Réalisé par le client (acceptance, clean-room tests)
S’assurer que le cahier des charges a été respecté.
3
Les tests unitaires
Test structurel (white-box test) :
L'implémentation de la classe doit être connue.
Test vérifiant si le code est robuste en contrôlant son comportement avec des
cas de test inattendus (cas limites).
Le but de ce test est d'exécuter chaque branche du code avec différentes
conditions d'entrée afin de détecter tous les comportements anormaux (test
"boîte blanche").
Exemple : nombre min, max, état d'une instance, dépassement de capacité,
etc.
Test de validation (black-box test) :
Ils s'effectuent sans connaissance de la structure interne (tests "boîte noire").
Test permettant de vérifier que les exigences fonctionnelles et techniques du
cahier des charges sont respectées.
Le test boîte noire vérifie la fonctionnalité de l'interface publique d'une unité.
4
Quelle catégorie de tests choisir ?
Les tests boîte blanche sont très difficiles à mettre
en oeuvre tandis que les tests boîte noire peuvent
être automatisés
De quels outils dispose t-on en Java ?
5
Quelle catégorie de tests choisir ?
Tests structurels :
assertions embarquées dans le code;
Pré/post conditions d'exécution d'une instruction;
issues des travaux de Tony Hoare sur les spécifications
algébriques pour la preuve de programme dans les
années 1960;
Tests boîtes noires :
Conception par contrats (design by contracts) Pré/post
conditions d'appel à des objets;
Assertions Java;
Outil d'exécution de tests unitaires (JUnit, Rational
Functional Tester, etc.);
6
Tests unitaires
7
Pourquoi JUnit ?
Tests unitaires
Traces
(instruction log) dans les programmes
Utilisation d’un debugger (possibilité de redéfinir
les expressions de test) sans avoir à recompiler les
programmes
Sévère limitations
nécessitent un jugement humain
problèmes de composition :
Une seule expression exécutable à la fois avec un debugger
Si une trace contient de nombreux print, perte de lisibilité
8
Le débutant…
TEST…
Tests embarqués dans la classe. Lancement et vérification manuels.
Le débutant…
AIE ! Plusieurs méthodes….
Le débutant…
Devient TRES complexe ….
Test dans les classes ?
C’est une mauvaise pratique car elle entraîne :
L'augmentation de l'entropie :
plus l'interface de la classe grandit, plus le programme
principal "main()" grandit (jusqu'à dépasser le code
fonctionnel);
Le code de production est plus important que
nécessaire;
Absence de sécurité (exemple: mot de passe en clair
dans le programme principal compilé).
…
12
Test dans les classes ?
C’est une mauvaise pratique car elle entraîne :
Une défaillance des tests :
…
La méthode "main()" appartient à la classe et accède
aux méthodes et membres privés.
Les clients de cette classe n'ont pas ce privilège =>
source d'erreur;
La plus petite unité à tester dans la classe est la
méthode : chaque méthode doit être testées
séparément pour détecter les cas d'erreur;
Chaque méthode doit être testée en dépit de tests
précédents défaillants afin de connaître le pourcentage
de tests défaillants.
13
Test dans les classes ?
C’est une mauvaise pratique car elle entraîne :
La difficulté d'automatiser le test :
Il est encore nécessaire de créer un script pour passer
des arguments à "main()";
Chaque classe à un programme spécifique avec son
code d'initialisation et des appels à des routines
spécifiques.
Un système de report d'anomalies maison doit être
défini.
14
JUnit
origine :
TDD (test-driven developpment), Xtreme
programming XP, méthodes agiles
Erich Gamma et Kent Beck
canevas (framework) ecrit en Java
open source : www.junit.org
intégration dans Eclipse et Maven ;
différences notables entre JUnit 3.8 et JUnit 4
JUnit 4 plus facile a utiliser ;
JUnit 3.8 plus facile a comprendre =>connaître les 2 !
15
JUnit
un ensemble d'interfaces et de classes qui
collaborent
qui ne s'utilisent pas directement
c'est une application (( a trous )) qui
offre la partie commune des traitements
et est spécialisée par chaque utilisateur
il faut implanter/spécialiser les types fournis
ex : pour créer un cas de test on hérite de la
classe TestCase
16
Tests unitaires
Classes de JUnit
17
Classes et méthodes de test
Classe de test
hérite de JUnit.framework.TestCase
contient les méthodes de test
peut utiliser les assertXXX sans import
constructeur a paramètre String (dépend des versions)
Méthodes de test
publique, type de retour void
le nom commence obligatoirement par test
pas de paramètre, peut lever une exception
contiennent des assertions
18
Ecriture d’une classe de test
à tester :
Tests unitaires
Classe
19
Ecriture d’une classe de test
de test :
Tests unitaires
Classe
Création
des objets qui vont interagir lors du test
Code qui agit sur les objets impliqués dans le test
Vérification que le résultat obtenu correspond bien au résultat
attendu
20
Ecriture d’une classe de test
static void assertTrue(boolean test)
méthode JUnit : vérifie que test == true et
dans le cas contraire lance une exception (en
fait une Error) de type
AssertionFailedError
L’exécuteur de tests JUnit attrape ces objets
Errors et indique les tests qui ont échoué
21
Les méthodes assert
static
void assertTrue(String message, boolean test)
Le message optionnel est inclus dans l’Error
void assertFalse(boolean test)
static void assertFalse(String message, boolean test)
static
vérifie que test == true
assertEquals(expected,
actual)
assertEquals(String message, expected, actual)
méthode largement surchargée: arg1 et arg2 doivent être tout deux
des objets ou bien du même type primitif
Pour les objets, utilise la méthode equals (public boolean equals(Object o)
sinon utilise ==
22
Les méthodes assert
expected, Object actual)
assertSame(String message, Object expected, Object actual)
assertSame(Object
Vérifie que expected et actual référencent le même objet (==)
expected, Object actual)
assertNotSame(String message, Object expected, Object actual)
assertNotSame(Object
Vérifie que expected et actual ne référencent pas le même objet (==)
object)
assertNull(String message, Object object)
assertNull(Object
Vérifie que objet est null
assertNotNull(Object object)
assertNotNull(String message, Object object)
Vérifie que objet n’est pas null
23
Les méthodes assert
fail()
fail(String
message)
Provoque l’échec du test et lance une AssertionFailedError
Utile lorsque les autres méthodes assert ne correspondent pas
exactement à vos besoins ou pour tester que certaines exceptions sont
bien lancées
try {
// appel d’une méthode devant lancer une Exception
fail("Did not throw an ExpectedException");
}
catch (ExpectedException e) { }
24
setup / tearDown
méthode
setUp
appelée avant chaque méthode de test
permet de factoriser la construction de (( l'état du monde ))
méthode
tearDown
appelée après chaque méthode de test
permet de défaire (( l'état du monde ))
25
Ecriture d’une classe de test
26
Exécution d’un test
Pour
exécuter un test
TestResult result = (new
CounterTest("testIncrement")).run();
Pour
exécuter une suite de tests
TestSuite suite = new TestSuite();
suite.addTest(new
CounterTest("testIncrement"));
suite.addTest(new
CounterTest("testDecrement"));
TestResult result = suite.run();
En utilisant l’introspection
TestSuite suite = new
TestSuite(CounterTest.class);
TestResult result = suite.run();
27
Résultats d’un test
JUnit propose des outils pour exécuter suite de
tests et afficher les résultats
Ajouter à la classe de test une méthode statique
suite pour rendre une suite accessible à un
"exécuteur de test" (TestRunner) :
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTest(new CounterTest("testIncrement"));
suite.addTest(new CounterTest("testDecrement"));
return suite;
}
Autre solution (introsepection)
junit.textui.TestRunner.run(CounterTest.class);
De
nombreux IDE intègrent JUnit : NetBeans,
Eclipse
28
Jouer tous les tests d’un package
TestSuite contient des objets Test
Des TestCase mais aussi des TestSuite
Une
classe AllTest
Teste toutes les suite de tests du package
public class AllTests {
public static Test suite() {
TestSuite suite =
new TestSuite("Test for default package");
suite.addTest(new TestSuite(TokenizerTest.class));
suite.addTest(new TestSuite(NodeTest.class));
suite.addTest(new TestSuite(ParserTest.class));
suite.addTest(new TestSuite(TreeTest.class));
return suite;
}
}
29
Jouer tous les tests de souspackage
Dans chaque package définir une
suite qui inclus les suites des sous
packages
public class AllTests_{
public static Test suite() {
TestSuite suite =
new TestSuite("All helix.* and subpackages tests");
suite.addTest(helix.edb.AllTests_.suite());
suite.addTest(helix.def.AllTests_.suite());
return suite;
}
}
30
JUnit 4
Différences notables dans l ’écriture
Même philosophie
utilise les @tags java 5
test unitaire
setup/teardown
…
Plus souple pour l’écriture
31
JUnit 4 - @Test
@Test
Mark your test cases with @Test annotations. You
don’t need to prefix your test cases with “test”. In
addition, your class does not need to extend from
“TestCase” class.
@Test
public void addition() {
assertEquals(12, simpleMath.add(7, 5));
}
@Test
public void subtraction() {
assertEquals(9, simpleMath.substract(12, 3));
}
32
JUnit 4 - @Before and @After
@Before and @After
Use @Before and @After annotations for “setup”
and “tearDown” methods respectively. They run
before and after every test case.
@Before
public void runBeforeEveryTest() {
simpleMath = new SimpleMath();
}
@After
public void runAfterEveryTest() {
simpleMath = null;
}
33
JUnit 4 – @BeforeClass and @AfterClass
@BeforeClass and @AfterClass
Use @BeforeClass and @AfterClass annotations for
class wide “setup” and “tearDown” respectively.
Think them as one time setup and tearDown. They
run for one time before and after all test cases.
@BeforeClass
public static void runBeforeClass() {
// run for one time before all test cases
}
@AfterClass
public static void runAfterClass() {
// run for one time after all test cases
}
34
JUnit 4 - Exception Handling
Exception Handling
Use “expected” paramater with @Test annotation
for test cases that expect exception. Write the class
name of the exception that will be thrown.
@Test(expected = ArithmeticException.class)
public void divisionWithException() {
// divide by zero
simpleMath.divide(1, 0);
}
35
JUnit 4 - @Ignore
@Ignore
Put @Ignore annotation for test cases you want to
ignore. You can add a string parameter that defines
the reason of ignorance if you want.
@Ignore("Not Ready to Run")
@Test
public void multiplication() {
assertEquals(15, simpleMath.multiply(3, 5));
}
36
JUnit 4 - Timeout
Timeout
Define a timeout period in milliseconds with
“timeout” parameter. The test fails when the
timeout period exceeds.
@Test(timeout = 1000)
public void infinity() {
while (true) ;
}
37
JUnit 4 - New Assertions
New Assertions
Compare arrays with new assertion methods. Two arrays
are equal if they have the same length and each element is
equal to the corresponding element in the other array;
otherwise, they’re not.
public static void assertEquals(Object[] expected, Object[] actual);
public static void assertEquals(String message, Object[] expected, Object[]
actual);
@Test
public void listEquality() {
List<Integer> expected = new ArrayList<Integer>();
expected.add(5);
List<Integer> actual = new ArrayList<Integer>();
actual.add(5);
assertEquals(expected, actual);
}
38
Test Tips
Code
a little, test a little, code a little, test a little . . .
Run your tests as often as possible, at least as often as you
run the compiler ☺
Begin by writing tests for the areas of the code that you’re the
most worried about . . .write tests that have the highest
possible return on your testing investment
When you need to add new functionality to the system, write
the tests first
If you find yourself debugging using System.out.println(),
write a test case instead
When a bug is reported, write a test case to expose the bug
Don’t deliver code that doesn’t pass all the tests
39
Principes
Integration
continue – Pendant le développement,
le programme marche toujours – peut être qu’il ne
fait pas tout ce qui est requis mais ce qu’il fait, il le fait
bien.
TDD Test Driven Development :pratique qui se
rattache à l’eXtreme Programming
“Any program feature without an automated test
simply doesn’t exist.” from Extreme
Programming Explained, Kent Beck
40
Tests : Références
www.junit.org
www.extremeprogramming.org
www.developpez.com
http://cruisecontrol.sourceforge.net/
41
Conclusion
Outils
d’aide au développement, en particulier
en groupe
Javadoc : indispensable ! Gain de temps
énorme pour la documentation....
Log : indispensable pour le suivit après mise
en production d’un logiciel
Asserts/Tests unitaires : assert moins utilisés,
souvent « déportés » vers tests unitaires...
Tests unitaires indispensables, exécutés en
continu pendant le projet, livrés avec le produit
final au client.
42