Introduction JUnit Assertions Exécution Introduction JUnit Assertions Exécution Programmation - Tests unitaires : JUNIT - 1 Introduction Présentation Framework de tests unitaires 2 JUnit TestCase Avant / Après les tests 3 Assertions Méthodes classiques Méthode basée sur des contrats 4 Exécution Cas de Test Suite de test Nicolas Malandain March 10, 2010 as as Architecture des Systèmes d’Information Tests Unitaires Introduction JUnit Assertions Exécution Architecture des Systèmes d’Information Tests Unitaires 1 / 27 Présentation Framework de tests unitaires Introduction JUnit Assertions Exécution Objectif des tests unitaires 2 / 27 Présentation Framework de tests unitaires xUnit S’assurer du bon fonctionnement d’une partie ou de la totalité d’un logiciel Bibliothèques xUnit disponibles pour de nombreux langages AUnit (Ada) JUnit (Java) Utilisation vérifier que le code répond aux spécification fonctionnelles FLEXUnit (Adobe Flex) NUnit (.NET) permettre de tester facilement le code JSUnit (Javascript) lors de modification de code, vérifier qu’il n’y a pas de régression CPPUnit (C++) Les tests unitaires étaient très utilisés pour des applications critiques. La méthode agile XP (eXtreme Programming) les place maintenant au centre du processus de développement Tests Unitaires 3 / 27 ... Tests Unitaires 4 / 27 Introduction JUnit Assertions Exécution Présentation Framework de tests unitaires Introduction JUnit Assertions Exécution TestCase Avant / Après les tests Le framework JUnit Principe des annotations en Java Créé par Kent Beck et Erich Gamma Méta-données ajout de méta-données (déclaratif) dans le code source qui seront accessibles à la compilation et à l’exécution (par réflexion). Principe un environnement de développement simple Deux exemples : un test = un objet public vérification “à la chaı̂ne” des tests depuis la version 4.x utilisation des annotations java } Tests Unitaires Introduction JUnit Assertions Exécution class A { public @Deprecated public void laMethode() { ... } public void methodeA() { ... } 5 / 27 class B extends A { @Override public void methodeA() { ... } } Tests Unitaires TestCase Avant / Après les tests Introduction JUnit Assertions Exécution 6 / 27 TestCase Avant / Après les tests Vocabulaire JUnit Test des méthodes d’une classe Test case (Cas de test) Principe création d’une classe de test pour chaque classe à tester annotation des méthodes de test par @Test classe regroupant un ensemble de tests généralement liés à une classe à tester Syntaxe (package org.junit) Test méthode effectuant un test import org.junit.Test public class TestDeLaClasse { Fixture contenu commun aux tests d’un cas de test @Test public void testConstructeur() { // utilisation d’assertions } Test Suite enchaı̂nement de cas de test @Test public void testMethode1() { // utilisation d’assertions } } Tests Unitaires 7 / 27 ... Tests Unitaires 8 / 27 Introduction JUnit Assertions Exécution TestCase Avant / Après les tests Introduction JUnit Assertions Exécution TestCase Avant / Après les tests Paramétrage de @Test Exécution de code avant et après chaque test @Test(timeout=durée ) Le test déclaré avec un timeout échoue si la méthode annotée prend plus de temps que la durée (ms) indiquée . Objectifs Préparer un contexte avant chaque test et le nettoyer après chaque test allocation/désallocation de ressources, initialisation d’objets “fixes” (fixtures) @Test(expected=ClasseException.class ) Exemples d’utilisation Le test déclaré avec une classe d’exception doit lancer cette exception sinon le test échoue. allocation de ressources avant un test (création d’un fichier) et suppression de ces ressources après le test (destruction d’un fichier) création d’objets commun à chaque test @Ignore Une annotation @Test précédée d’un @Ignore permet de ne pas passer le test. Tests Unitaires Introduction JUnit Assertions Exécution Syntaxe Utilisation des annotations @Before et @After devant les méthodes correspondantes 9 / 27 Tests Unitaires TestCase Avant / Après les tests Introduction JUnit Assertions Exécution Exécution de code avant et après chaque cas de test 10 / 27 Méthodes classiques Méthode basée sur des contrats Présentation des assertions Assertion (linguistique/philosophique) “Une assertion représente un énoncé considéré ou présenté comme vrai”a Objectif Initialisation d’objets fixes commun aux tests : objets constant, singleton, ou encore ayant un temps de préparation très long. Syntaxe Annotations de méthodes public static void avec @BeforeClass et @AfterClass. a wikipedia Assertion (mathématique/logique) “Une assertion est une proposition mathématique vraie. Cette proposition vraie s’inscrit dans le cadre d’une théorie précisée”a a wikipedia D’un point de vue génie logiciel Les assertions permettent de vérifier des hypothèses de conceptiona a Tests Unitaires 11 / 27 Java in a Nutshell, Fifth edition (O’REILLY) Tests Unitaires 12 / 27 Introduction JUnit Assertions Exécution Les assertions de base de java Méthodes classiques Méthode basée sur des contrats Introduction JUnit Assertions Exécution classe org.junit.Assert Méthodes classiques Méthode basée sur des contrats Exemple avec assertions classiques 1 / 3 TestAmpouleClassique.java import import import import import import Quelques méthodes de classe : assertEquals(..., ...) / assertArrayEquals(..., ...) teste l’égalité des types simples, des objets et des tableaux assertSame(Object, Object) teste qu’il s’agit du même objet public Ampoule ampoule; @Before public void avantTest() { ampoule = new Ampoule(); } ... Remarque : un commentaire (String) d’assertion peut être passer en 1◦ paramètre pour expliquer la raison de l’échec d’une assertion Introduction JUnit Assertions Exécution @Test(timeout=1000) public void test_basculer() { boolean etat = ampoule.getEtat(); assertFalse(etat); assertTrue(ampoule.basculer()); assertTrue(ampoule.getEtat()); while (etat != ampoule.getEtat()) { etat=ampoule.getEtat(); ampoule.basculer(); } assertFalse(ampoule.getEtat()); } public class TestAmpouleClassique { assertTrue(boolean)/assertFalse(boolean) teste le résultat d’une expression logique Tests Unitaires @Ignore @Test public void test_SetGetCouleur() { ampoule.setCouleur(Color.RED); assertEquals(ampoule.getCouleur(), Color.BLUE); } org.junit.After; static org.junit.Assert.*; org.junit.Before; org.junit.Ignore; org.junit.Test; java.awt.Color; @Test public void test_construteurs() { assertEquals(ampoule.getCouleur(), Color.WHITE); } assertFalse(ampoule.getEtat()); assertEquals((new Ampoule(Color.BLUE)).getCouleur(),Color.BLUE); } 13 / 27 Méthodes classiques Méthode basée sur des contrats Tests Unitaires Introduction JUnit Assertions Exécution Exemple avec assertions classiques 2 / 3 14 / 27 Méthodes classiques Méthode basée sur des contrats Exemple avec assertions classiques 3 / 3 Exécution Échec : ( @Ignore ) JUnit version 4.5 .Before After .Before After E.Before After Exécution OK JUnit version 4.5 .Before After I.Before After Time: 0,023 There was 1 failure: 1) test_SetGetCouleur(TestAmpouleClassique) java.lang.AssertionError: expected:<java.awt.Color[r=255,g=0,b=0]> but was:<java.awt.Color[r=0,g=0,b=255]> at org.junit.Assert.fail(Assert.java:91) at org.junit.Assert.failNotEquals(Assert.java:618) ... at org.junit.runners.ParentRunner.run(ParentRunner.java:220) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at org.junit.runner.JUnitCore.run(JUnitCore.java:116) at org.junit.runner.JUnitCore.run(JUnitCore.java:107) at org.junit.runner.JUnitCore.runMain(JUnitCore.java:88) at org.junit.runner.JUnitCore.runMainAndExit(JUnitCore.java:54) at org.junit.runner.JUnitCore.main(JUnitCore.java:46) Time: 0,017 OK (1 test) FAILURES!!! Tests run: 3, Failures: 1 Tests Unitaires 15 / 27 Tests Unitaires 16 / 27 Introduction JUnit Assertions Exécution Méthodes classiques Méthode basée sur des contrats Introduction JUnit Assertions Exécution Remarques 1 / 2 Méthodes classiques Méthode basée sur des contrats Remarques 2 / 2 Comparaison de 2 valeurs @Test public void testNonExplicite1() { int x = 5; assertTrue(x>10); } @Test public void testExpliciteAMaintenir() { int x = 5; assertTrue(x+" n’est pas supérieur à 10",x>10); } Constations par défaut pas de message explicite de l’échec comment assurer la cohérence du message avec l’assertion dans le temps ? rq : il y a redondance entre l’assertion et le message pas de réelles combinaisons d’assertions possible Exécution 1) testNonExplicite1(TestNonExplicite) java.lang.AssertionError: at org.junit.Assert.fail(Assert.java:91) at org.junit.Assert.assertTrue(Assert.java:43) ... 2) testExpliciteAMaintenir(TestNonExplicite) java.lang.AssertionError: 5 n’est pas supérieur à 10 at org.junit.Assert.fail(Assert.java:91) at org.junit.Assert.assertTrue(Assert.java:43) Tests Unitaires Introduction JUnit Assertions Exécution =⇒ les assertions à base de contrat Méthodes classiques Méthode basée sur des contrats Introduction JUnit Assertions Exécution Les assertions basées sur des contrats 1 / 2 18 / 27 Méthodes classiques Méthode basée sur des contrats Les assertions basées sur des contrats 2 / 2 La bibliothèque JMock API permettant d’énoncer des contraintes sur la valeur d’un objet et de les combiner. Bibliothèque découpée en 7 parties : org.hamcrest.beans org.hamcrest.collection org.hamcrest.core org.hamcrest.number org.hamcrest.object org.hamcrest.text Insertion de JMock dans JUnit 4 ajout de org.hamcrest.core dans JUnit. Contrats disponibles : AllOf IsEqual AnyOf IsInstanceOf DescribedAs IsNot Is IsNull IsAnything IsSame Déclaration des contrats avec la classe Assert org.hamcrest.xml Tests Unitaires Tests Unitaires 17 / 27 public static <T> void assertThat(T actual, org.hamcrest.Matcher<T> matcher) 19 / 27 Tests Unitaires 20 / 27 Introduction JUnit Assertions Exécution Méthodes classiques Méthode basée sur des contrats Introduction JUnit Assertions Exécution Exemple avec assertions à contrats 1 / 1 Méthodes classiques Méthode basée sur des contrats Des messages explicites TestAmpoule.java Test de comparaison import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.hamcrest.core.IsEqual; import org.junit.Test; import java.awt.Color; // Attention import static import static org.hamcrest.core.Is.is; import static org.junit.Assert.*; public class TestAmpoule { public Ampoule ampoule; @Before public void avantTest() { ampoule = new Ampoule(); } @Test public void test_SetGetCouleur() { ampoule.setCouleur(Color.RED); assertThat(ampoule.getCouleur(), IsEqual.equalTo(Color.RED)); } import org.hamcrest.number.OrderingComparisons; ... @Test public void testExplicite() { int x = 50; assertThat(x, OrderingComparisons.lessThan(10)); } @Test(timeout=1000) public void test_basculer() { boolean etat = ampoule.getEtat(); assertThat(etat, is(false)); assertThat(ampoule.basculer(), is(true)); assertThat(ampoule.getEtat(), is(true)); while (etat != ampoule.getEtat()) { etat=ampoule.getEtat(); ampoule.basculer(); } assertThat(ampoule.getEtat(), is(false)); } Test de comparaison en erreur java.lang.AssertionError: Expected: a value less than <10> got: <50> @Test } public void test_construteurs() { assertThat(ampoule.getCouleur(), IsEqual.equalTo(Color.WHITE)); assertThat(ampoule.getEtat(), is(false)); assertThat((new Ampoule(Color.BLUE)).getCouleur(), IsEqual.equalTo(Color.BLUE)); } Tests Unitaires Introduction JUnit Assertions Exécution ... at at at at at org.junit.Assert.assertThat(Assert.java:750) org.junit.Assert.assertThat(Assert.java:709) TestNonExplicite.testExplicite(TestNonExplicite.java:29) sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) Tests Unitaires 21 / 27 Méthodes classiques Méthode basée sur des contrats Introduction JUnit Assertions Exécution Exemples de contrats 22 / 27 Méthodes classiques Méthode basée sur des contrats Exemple de combinaison de contrats Code import import import import import import ... Contrats de base IsSame.sameInstance(...) IsEqual.equalTo(...) IsInstanceOf.instanceOf(...) Is.is(...) comportement selon la valeur (classe, égalité, contrat) Combinaison de contrats AllOf.allOf( les contrats ) @Test public void testCombinaisonContrats() { int x = 150; assertThat(x, anyOf(allOf(lessThan(100), greaterThan(50)), is(42))); } Exécution There was 1 failure: 1) testCombinaisonContrats(TestNonExplicite) java.lang.AssertionError: Expected: ((a value less than <100> and a value greater than <50>) or is <42>) got: <150> AnyOf.anyOf( les contrats ) ... Tests Unitaires static org.junit.Assert.*; static org.hamcrest.core.Is.is; static org.hamcrest.core.AllOf.allOf; static org.hamcrest.core.AnyOf.anyOf; org.junit.Test; static org.hamcrest.number.OrderingComparisons.*; 23 / 27 at org.junit.Assert.assertThat(Assert.java:750) at org.junit.Assert.assertThat(Assert.java:709) Tests Unitaires 24 / 27 Introduction JUnit Assertions Exécution Cas de Test Suite de test Introduction JUnit Assertions Exécution Exécution d’un cas de test Cas de Test Suite de test Déclaration d’une suite Les annotations @RunWith et @SuiteClasses import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; Ajout au CLASSPATH JUnit /chemin-junit /junit-4.5.jar @RunWith(Suite.class) @SuiteClasses({ CasDeTest1 ou Suite1 , CasDeTest2 ou Suite2 , ... }) JMock /chemin-jmock /jmock-2.5.1.jar /chemin-jmock /hamcrest-core-1.1.jar /chemin-jmock /hamcrest-library-1.1.jar public class NomDeLaSuite { // classe vide } Appel java org.junit.runner.JUnitCore casdetest Exécution d’une Suite java org.junit.runner.JUnitCore NomDeLaSuite Tests Unitaires Introduction JUnit Assertions Exécution 25 / 27 Cas de Test Suite de test Exemple SuiteA.java SuiteB.java import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @SuiteClasses({ Test1A.class, Test2A.class }) public class SuiteA {} @RunWith(Suite.class) @SuiteClasses({ Test1B.class, Test2B.class, Test3B.class }) public class SuiteB {} AllTests.java import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @Suite.SuiteClasses({ SuiteA.class, SuiteB.class }) public class AllTests {} Tests Unitaires 27 / 27 Tests Unitaires 26 / 27