JUnit, un framework de test unitaire pour Java
Transcription
JUnit, un framework de test unitaire pour Java
JUnit, un framework de test unitaire pour Java JUnit, un framework de test unitaire pour Java Clémentine Nebut LIRMM / Université de Montpellier 26 Septembre 2016 JUnit, un framework de test unitaire pour Java Introduction 1 Introduction 2 Les bases 3 Approfondissements Les asssertions Le test paramétré Les suites de test Autre exemple issu de http ://www.devx.com/Java/Article/31983 4 JUnit et introspection 5 Conclusion JUnit, un framework de test unitaire pour Java Introduction Sommaire 1 Introduction 2 Les bases 3 Approfondissements Les asssertions Le test paramétré Les suites de test Autre exemple issu de http ://www.devx.com/Java/Article/31983 4 JUnit et introspection 5 Conclusion JUnit, un framework de test unitaire pour Java Introduction JUNit Origine Xtreme programming (test-rst development), méthodes agiles framework de test écrit en Java par E. Gamma et K. Beck open source : www.junit.org Objectifs test d'applications en Java faciliter la création des tests tests de non régression JUnit, un framework de test unitaire pour Java Introduction Ce que fait JUnit Enchaîne l'exécution des méthodes de test dénies par le testeur Facilite la dénition des tests grâce à des assertions, des méthodes d'initialisation et de nalisation Permet en un seul clic de savoir quels tests ont échoué/planté/réussi JUnit (et au delà xUnit) est de facto devenu un standard en matière de test JUnit, un framework de test unitaire pour Java Introduction Ce que ne fait pas JUnit JUnit n'écrit pas les tests ! Il ne fait que les lancer. JUnit ne propose pas de principes/méthodes pour structurer les tests JUnit, un framework de test unitaire pour Java Introduction JUnit : un framework Le framework dénit toute l'infrastructure nécessaire pour : écrire des tests dénir leurs oracles lancer les tests Utiliser Junit : dénir les tests s'en remettre à JUnit pour leur exécution ne pas appeler explicitement les méthodes de test JUnit, un framework de test unitaire pour Java Introduction JUnit : versions initiales et versions ≥4 Versions initiales Paramétrage par spécialisation Utilisation de conventions de nommage Versions ≥4 Utilisation d'annotations beaucoup de nouvelles fonctionalités dans JUnit 4 attention, la plupart des docs trouvées sur internet se basent sur junit 3 pas de runner graphique en version 4, laissé au soin des IDEs JUnit, un framework de test unitaire pour Java Les bases Sommaire 1 Introduction 2 Les bases 3 Approfondissements Les asssertions Le test paramétré Les suites de test Autre exemple issu de http ://www.devx.com/Java/Article/31983 4 JUnit et introspection 5 Conclusion JUnit, un framework de test unitaire pour Java Les bases Écriture de test : principe général On crée une ou plusieurs classes destinées à contenir les tests : les classes de test. On y insère des méthodes de test. Une méthode de test fait appel à une ou plusieurs méthodes du système à tester, ce qui suppose d'avoir une instance d'une classe du système à tester (la création d'une telle instance peut être placée à plusieurs endroits, voir plus loin), inclut des instructions permettant un verdict automatique : les assertions. JUnit, un framework de test unitaire pour Java Les bases Classe de test Contient les méthodes de test Est une collection de cas de test (sans ordre) peut contenir des méthodes particulières pour positionner l'environnement de test En JUnit : Junit versions <4 : la classe de test hérite de JUnit.framework.TestCase JUnit versions ≥4 : une classe quelconque JUnit, un framework de test unitaire pour Java Les bases Cas de test / méthode de test s'intéresse à une seule unité de code/ un seul comportement doit rester court les cas de test sont indépendants les uns des autres Avec Junit, un cas de test ≡ une méthode (méthode de test) Junit versions <4 : les méthodes de test commencent par le mot test JUnit versions ≥4 : annotées @Test les méthodes de test seront appelées par Junit, dans un ordre supposé quelconque. JUnit, un framework de test unitaire pour Java Les bases Les méthodes de test sont sans paramètres et sans type de retour (logique puisqu'elles vont être appelées automatiquement par JUnit) embarquent l'oracle i.e. contiennent des assertions x vaut 3 le résultat de l'appel de telle méthode est non nul x est plus petit que y JUnit, un framework de test unitaire pour Java Les bases Les verdicts Sont dénis grâce aux assertions placées dans les cas de test. Pass (vert) : pas de faute détectée Fail (rouge) : échec, on attendait un résultat, on en a eu un autre Error : le test n'a pas pû s'exécuter correctement (exception inattendue, ...) En JUnit 4, plus de diérence entre fail et error JUnit, un framework de test unitaire pour Java Les bases Exemple en version 4 classe à tester http ://code.google.com/p/t2framework/wiki/JUnitQuickTutorial public class Subscription { p r i v a t e i n t p r i c e ; // s u b s c r i p t i o n t o t a l p r i c e i n euro −c e n t p r i v a t e i n t l e n g t h ; // l e n g t h o f s u b s c r i p t i o n i n months // c o n s t r u c t o r : public Subscription ( int p , int n) { price = p ; length = n ; } / ∗∗ ∗ C a l c u l a t e the monthly s u b s c r i p t i o n ∗ r o u n d e d up t o t h e n e a r e s t c e n t . ∗/ p r i c e i n euro , p u b l i c double pricePerMonth () { double r = ( double ) p r i c e / ( double ) length ; return r ; } / ∗∗ ∗ Call ∗/ t h i s to c a n c e l / n u l i f y t h i s s u b s c r i p t i o n . public void cancel () { length = 0 ; } } JUnit, un framework de test unitaire pour Java Les bases Exemple en version 4 objectif de test http ://code.google.com/p/t2framework/wiki/JUnitQuickTutorial If we have a subscription of 200 cent for a period of 2 month, its monthly price should be 1 euro, right ? The monthly price is supposed to be rounded up to the nearest cent. So, if we have a subscription of 200 cent for a period of 3 month, its monthly price should be 0.67 euro. JUnit, un framework de test unitaire pour Java Les bases Exemple en version 4 classe de test http ://code.google.com/p/t2framework/wiki/JUnitQuickTutorial import org . j u n i t .∗ ; import s t a t i c org . j u n i t . Assert .∗ ; public class SubscriptionTest { @Test public void test_returnEuro () { System . o u t . p r i n t l n ( " T e s t i f p r i c e P e r M o n t h r e t u r n s Euro . . . " ) ; S u b s c r i p t i o n S = new S u b s c r i p t i o n ( 2 0 0 , 2 ) ; a s s e r t T r u e ( S . p r i c e P e r M o n t h ( ) == 1 . 0 ) ; } } @Test p u b l i c v o i d test_roundUp ( ) { System . o u t . p r i n t l n ( " T e s t i f p r i c e P e r M o n t h r o u n d s up c o r r e c t l y . . . " ) ; S u b s c r i p t i o n S = new S u b s c r i p t i o n ( 2 0 0 , 3 ) ; a s s e r t T r u e ( S . p r i c e P e r M o n t h ( ) == 0 . 6 7 ) ; } JUnit, un framework de test unitaire pour Java Les bases L'environnement de test Les méthodes de test ont besoin d'être appelées sur des instances Déclaration et création des instances en général, les instances sont déclarées comme membres d'instance de la classe de test la création des instances et plus globalement la mise en place de l'environnement de test est laissé à la charge de méthodes d'initialisation NB : ce n'est pas ce qui a été fait dans l'exemple précédent. JUnit, un framework de test unitaire pour Java Les bases Préambules et postambules Méthodes écrites par le testeur pour mettre en place l'environnement de test. JUnit 4 : Méthodes avec annotations @Before et @After ; JUnit 3 : Méthodes appelées setUp et tearDown exécutées avant/après chaque méthode de test (l'exécution est pilotée par le framework, et pas le testeur) possibilité d'annoter plusieurs méthodes (ordre d'exécution indéterminé) publiques et non statiques Méthodes avec annotations @BeforeClass et @AfterClass (pas en JUnit 3) exécutées avant (resp. après) la première (resp. dernière) méthode de test une seule méthode pour chaque annotation publiques et statiques JUnit, un framework de test unitaire pour Java Approfondissements Sommaire 1 Introduction 2 Les bases 3 Approfondissements Les asssertions Le test paramétré Les suites de test Autre exemple issu de http ://www.devx.com/Java/Article/31983 4 JUnit et introspection 5 Conclusion JUnit, un framework de test unitaire pour Java Approfondissements Détails sur les méthodes de test en JUnit ≥ 4 L'annotation @Test peut prendre en paramètre : le type d'exception attendue @Test(expected=monexception.class) Succès ssi cette exception est lancée. un timeout : @Test (timeout=10) (en ms). Fail si la réponse n'arrive pas avant le timeout. annotation @ignore (paramètre optionnel : du texte) pour ignorer le test JUnit, un framework de test unitaire pour Java Approfondissements Les asssertions Sommaire 1 Introduction 2 Les bases 3 Approfondissements Les asssertions Le test paramétré Les suites de test Autre exemple issu de http ://www.devx.com/Java/Article/31983 4 JUnit et introspection 5 Conclusion JUnit, un framework de test unitaire pour Java Approfondissements Les asssertions Les assertions (en JUnit 4) Permettent d'embarquer et d'automatiser l'oracle dans les cas de test (adieu, println ...) Utilisation de org.junit.Assert.* attention, import statique, car les asserts sont des méthodes statiques import static org.junit.Assert.* ; Lancent des exceptions de type java.lang.AssertionError (comme les assert java classiques) Diérentes assertions : comparaison à un delta près, comparaison de tableaux (arrays), ... Forte surcharge des méthodes d'assertion. JUnit, un framework de test unitaire pour Java Approfondissements Les asssertions Assert that (nouveauté version 4.4) assertThat([value], [matcher statement]); exemples : assertThat(x, is(3)) ; assertThat(x, is(not(4))) ; assertThat(responseString, either(containsString("color")).or(containsString("colour"))) ; assertThat(myList, hasItem("3")) ; not(s), either(s).or(ss), each(s) Messages d'erreur plus clairs http ://junit.sourceforge.net/doc/ReleaseNotes4.4.html JUnit, un framework de test unitaire pour Java Approfondissements Les asssertions Assumptions (nouveauté version 4.4) AssumeThat(File.separatorChar, is(/)) L'assertion suivante sera ignorée si la supposition n'est pas vériée JUnit, un framework de test unitaire pour Java Approfondissements Le test paramétré Sommaire 1 Introduction 2 Les bases 3 Approfondissements Les asssertions Le test paramétré Les suites de test Autre exemple issu de http ://www.devx.com/Java/Article/31983 4 JUnit et introspection 5 Conclusion JUnit, un framework de test unitaire pour Java Approfondissements Le test paramétré Test paramétré Objectif : réutiliser une méthode de test avec des jeux de données de test diérents Jeux de données de test retournés par une méthode annotée @Parameters cette méthode retourne une collection de tableaux contenant les données et éventuellement le résultat attendu La classe de test annotée @RunWith(Parameterized.class) contient des méthodes devant être exécutées avec chacun des jeux de données Pour chaque donnée, la classe est instanciée, les méthodes de test sont exécutées JUnit, un framework de test unitaire pour Java Approfondissements Le test paramétré Test paramétré : les besoins Un constructeur public qui utilise les paramètres (i.e. un jeu de données quelconque) La méthode qui retourne les paramètres (i.e. les jeux de données) doit être statique JUnit, un framework de test unitaire pour Java Approfondissements Le test paramétré Exemple de test paramétré : test de Sum :int sum(int x, int y) import org . j u n i t . Test ; i m p o r t o r g . j u n i t . r u n n e r . RunWith ; import org . j u n i t . runners . Parameterized ; import org . j u n i t . runners . Parameterized . Parameters ; import s t a t i c org . j u n i t . Assert . ∗ ; import java . u t i l . ∗ ; @RunWith ( P a r a m e t e r i z e d . c l a s s ) p u b l i c c l a s s TestParamSum { private int x ; private int y ; private int res ; p u b l i c TestParamSum ( i n t x , i n t y , i n t r e s ) { this .x = x; this .y = y; this . res = res ;} @Parameters public s t a t i c Collection testData () { r e t u r n A r r a y s . a s L i s t ( new O b j e c t [ ] [ ] { { 0 , 0 , 0 } ,{ 1 , 1 , 2 } ,{ 2 , 1 , 3 } ,{10 , 9 , 19}});} @ T e s t p u b l i c v o i d testSum ( ) { a s s e r t E q u a l s ( r e s , new Sum ( ) . sum ( x , y ) ) ; } } JUnit, un framework de test unitaire pour Java Approfondissements Les suites de test Sommaire 1 Introduction 2 Les bases 3 Approfondissements Les asssertions Le test paramétré Les suites de test Autre exemple issu de http ://www.devx.com/Java/Article/31983 4 JUnit et introspection 5 Conclusion JUnit, un framework de test unitaire pour Java Approfondissements Les suites de test Suite de tests Rassemble des cas de test pour enchaîner leur exécution i.e. groupe l'exécution de classes de test i m p o r t o r g . j u n i t . r u n n e r . RunWith ; import org . j u n i t . runners . Suite ; @RunWith ( S u i t e . c l a s s ) @Suite . S u i t e C l a s s e s ({ ClasseDeTest1 . class , ClasseDeTest2 . c l a s s }) p u b l i c c l a s s MaSuiteDeTest { } JUnit, un framework de test unitaire pour Java Approfondissements Autre exemple Sommaire 1 Introduction 2 Les bases 3 Approfondissements Les asssertions Le test paramétré Les suites de test Autre exemple issu de http ://www.devx.com/Java/Article/31983 4 JUnit et introspection 5 Conclusion JUnit, un framework de test unitaire pour Java Approfondissements Autre exemple Classe à tester package c a l c ; public class Calculator { p r i v a t e s t a t i c i n t r e s u l t ; / / S t a t i c v a r i a b l e where t h e r e s u l t i s s t o r e d p u b l i c v o i d add ( i n t n ) { result = result + n; } public void substract ( int n) { r e s u l t = r e s u l t − 1; //Bug : s h o u l d be r e s u l t = r e s u l t − n } p u b l i c v o i d m u l t i p l y ( i n t n ) {} // Not i m p l e m e n t e d y e t public void divide ( int n) { result = result / n; } public void square ( i n t n) { result = n ∗ n; } p u b l i c void squareRoot ( i n t n) { for (; ;) ; //Bug : l o o p s i n d e f i n i t e l y } public void clear () { // C l e a n s t h e r e s u l t r e s u l t = 0; } p u b l i c v o i d s w i t c h O n ( ) {// S w i t h on t h e s c r e e n , d i s p l a y " h e l l o " , beep r e s u l t = 0; // and do o t h e r t h i n g s t h a t c a l c u l a t o r do nowadays } p u b l i c v o i d s w i t c h O f f ( ) { }// D i s p l a y " bye bye " , beep , s w i t c h o f f t h e s c r e e n public int getResult () { return result ; } JUnit, un framework de test unitaire pour Java Approfondissements Autre exemple Une classe de test import c a l c . C a l c u l a t o r ; import org . j u n i t . Before ; import org . j u n i t . Ignore ; import org . j u n i t . Test ; import s t a t i c org . j u n i t . Assert . ∗ ; public class CalculatorTest { p r i v a t e s t a t i c C a l c u l a t o r c a l c u l a t o r = new C a l c u l a t o r ( ) ; @Before public void clearCalculator () { calculator . clear (); } @Test p u b l i c v o i d add ( ) { c a l c u l a t o r . add ( 1 ) ; c a l c u l a t o r . add ( 1 ) ; assertEquals ( calculator . getResult () , 2); } @Test public void subtract () { c a l c u l a t o r . add ( 1 0 ) ; calculator . subtract (2); assertEquals ( calculator . getResult () , 8); } JUnit, un framework de test unitaire pour Java Approfondissements Autre exemple suite } @Test public void divide () { c a l c u l a t o r . add ( 8 ) ; calculator . divide (2); a s s e r t c a l c u l a t o r . g e t R e s u l t ( ) == 5 ; } @Test ( e x p e c t e d = A r i t h m e t i c E x c e p t i o n . c l a s s ) public void divideByZero () { calculator . divide (0); } @Ignore (" not ready yet ") @Test public void multiply () { c a l c u l a t o r . add ( 1 0 ) ; calculator . multiply (10); assertEquals ( calculator . getResult () , 100); } JUnit, un framework de test unitaire pour Java Approfondissements Autre exemple Une autre classe de test import c a l c . C a l c u l a t o r ; import org . j u n i t . A f t e r C l a s s ; import org . j u n i t . Before ; import org . j u n i t . BeforeClass ; import org . j u n i t . Test ; import s t a t i c org . j u n i t . Assert . ∗ ; p u b l i c c l a s s AdvancedTest e x t e n d s A b s t r a c t P a r e n t { private static Calculator calculator ; @BeforeClass public s t a t i c void switchOnCalculator () { System . o u t . p r i n t l n ( " S w i t c h on c a l c u l a t o r " ) ; c a l c u l a t o r = new C a l c u l a t o r ( ) ; c a l c u l a t o r . switchOn ( ) ; } @AfterClass public s t a t i c void switchOffCalculator () { System . o u t . p r i n t l n ( " S w i t c h o f f c a l c u l a t o r " ) ; calculator . switchOff ( ) ; calculator = null ; } @Before public void clearCalculator () { System . o u t . p r i n t l n ( " C l e a r c a l c u l a t o r " ) ; calculator . clear (); } JUnit, un framework de test unitaire pour Java Approfondissements Autre exemple suite @Test ( t i m e o u t = 1 0 0 0 ) p u b l i c void squareRoot () { c a l c u l a t o r . squareRoot ( 2 ) ; } @Test public void square2 () { c a l c u l a t o r . square (2); assertEquals (4 , c a l c u l a t o r . getResult ( ) ) ; } @Test public void square4 () { c a l c u l a t o r . square (4); a s s e r t E q u a l s (16 , c a l c u l a t o r . getResult ( ) ) ; } @Test public void square5 () { c a l c u l a t o r . square (5); a s s e r t E q u a l s (25 , c a l c u l a t o r . getResult ( ) ) ; } } JUnit, un framework de test unitaire pour Java Approfondissements Autre exemple suite package j u n i t 4 ; import org . j u n i t . ∗ ; public abstract c la s s AbstractParent { @BeforeClass public s t a t i c void startTestSystem () { System . o u t . p r i n t l n ( " S t a r t t e s t s y s t e m " ) ; } @AfterClass p u b l i c s t a t i c void stopTestSystem () { System . o u t . p r i n t l n ( " Stop t e s t s y s t e m " ) ; } @Before public void initTestSystem () { System . o u t . p r i n t l n ( " I n i t i a l i z e t e s t s y s t e m " ) ; } JUnit, un framework de test unitaire pour Java Approfondissements Autre exemple Avec tests paramétrés import c a l c . C a l c u l a t o r ; import s t a t i c org . j u n i t . Assert . a s s e r t E q u a l s ; import org . j u n i t . Test ; i m p o r t o r g . j u n i t . r u n n e r . RunWith ; import org . j u n i t . runners . Parameterized ; import org . j u n i t . runners . Parameterized . Parameters ; import java . u t i l . Arrays ; import java . u t i l . C o l l e c t i o n ; @RunWith ( P a r a m e t e r i z e d . c l a s s ) p u b l i c c l a s s SquareTest { p r i v a t e s t a t i c C a l c u l a t o r c a l c u l a t o r = new C a l c u l a t o r ( ) ; p r i v a t e i n t param ; private int result ; @Parameters p u b l i c s t a t i c C o l l e c t i o n data () { r e t u r n A r r a y s . a s L i s t ( new O b j e c t [ ] [ ] { {0 , 0} , {1 , 1} , {2 , 4} , {4 , 16} , { 5 , 2 5 } , { 6 , 3 6 } , { 7 , 49} }); } p u b l i c S q u a r e T e s t ( i n t param , i n t r e s u l t ) { t h i s . param = param ; this . result = result ; } @Test public void square () { c a l c u l a t o r . s q u a r e ( param ) ; assertEquals ( result , calculator . getResult ()); JUnit, un framework de test unitaire pour Java Approfondissements Autre exemple Une Suite de tests qui regroupe les 2 classes de test i m p o r t o r g . j u n i t . r u n n e r . RunWith ; import org . j u n i t . runners . Suite ; @RunWith ( S u i t e . c l a s s ) @Suite . S u i t e C l a s s e s ({ CalculatorTest . class , SquareTest . c l a s s }) public class AllCalculatorTests { } JUnit, un framework de test unitaire pour Java JUnit et introspection Sommaire 1 Introduction 2 Les bases 3 Approfondissements Les asssertions Le test paramétré Les suites de test Autre exemple issu de http ://www.devx.com/Java/Article/31983 4 JUnit et introspection 5 Conclusion JUnit, un framework de test unitaire pour Java JUnit et introspection JUnit : un framework Extraits de code de collecte des méthodes de test, JUnit 3 p u b l i c T e s t S u i t e ( f i n a l C l a s s t h e C l a s s ){ ... Method [ ] = t h e C l a s s . g e t D e c l a r e d M e t h o d s ... } p r i v a t e b o o l e a n i s T e s t M e t h o d ( Method m) { S t r i n g name= m. getName ( ) ; C l a s s [ ] p a r a m e t e r s= m. g e t P a r a m e t e r T y p e s ( ) ; C l a s s r e t u r n T y p e= m. g e t R e t u r n T y p e ( ) ; r e t u r n p a r a m e t e r s . l e n g t h == 0 && name . s t a r t s W i t h ( " t e s t " ) && r e t u r n T y p e . e q u a l s ( Void . TYPE ) ; } JUnit, un framework de test unitaire pour Java JUnit et introspection JUnit : un framework Extraits de code de collecte des méthodes de test, JUnit 3 Dans BlockJUnit4ClassRunner p r o t e c t e d L i s t <FrameworkMethod> computeTestMethods ( ) { r e t u r n g e t T e s t C l a s s ( ) . getAnnotatedMethods ( Test . c l a s s ) ; } Dans TestClass.java p u b l i c L i s t <FrameworkMethod> g e t A n n o t a t e d M e t h o d s ( C l a s s <? e x t e n d s A n n o t a t i o n > a n n o t a t i o n C l a s s ) { r e t u r n getAnnotatedMembers ( f M e t h o d s F o r A n n o t a t i o n s , a n n o t a t i o n C l a s s ) ; } JUnit, un framework de test unitaire pour Java Conclusion Sommaire 1 Introduction 2 Les bases 3 Approfondissements Les asssertions Le test paramétré Les suites de test Autre exemple issu de http ://www.devx.com/Java/Article/31983 4 JUnit et introspection 5 Conclusion JUnit, un framework de test unitaire pour Java Conclusion Conclusion sur JUnit Construction rapide de tests Exécution rapide Très bien adapté pour le test unitaire et test de non régression JUnit, un framework de test unitaire pour Java Conclusion JUnit et les autres NUnit -> .net PiUnit -> python FlexUnit -> Flex etc ...