Junit et les asserts
Je ne vais pas présenter Junit que tout développeur est sensé connaître mais je vais plus m’attarder sur les assertions que l’on fait à la suite de l’exécution des tests. Junit 4 propose deux classes pour vérifier les insertions pour tous les types et objets primitifs
- junit.framework.Assert
- org.junit.Assert
Pour récupérer Junit dans un repo Maven voici la description de l’artefact
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> </dependency>
Revenons aux assertions, le principe est de faire échouer la méthode de test si l’assertion n’est pas vérfiée. En Junit on aura
// JUnit assertEquals(Object expected, Object actual) assertSame(Object expected, Object actual) assertNotSame(Object expected, Object actual) assertNull(Object object) assertNotNull(Object object) assertTrue(boolean condition) assertFalse(boolean condition) fail(String)
Ces méthodes s’appliquent pour tous les types primitifs et vous pouvez ajouter une chaine de caractères pour spécifier le message qui sera retourné
Dans la version 4 (org.junit.Assert) des méthode pour comparer des tableaux ont été ajoutées
assertArrayEquals(Object[] expecteds, Object[] actuals)
Un exemple
Framework fwk = classeATester.getById(1L); Assert.assertEquals(Long.valueOf(1), fwk.getId()); Assert.assertEquals(ProgrammingLanguage.JAVA, fwk.getLanguage()); Assert.assertEquals("Junit", fwk.getName()); Assert.assertEquals("4.11", fwk.getVersion());
Mettons de côté la technique et regardons comment on traduit en français ce type de message
- Est ce que égaux sont les objet attendu et objet actuel ?
- Est ce que null est mon objet ?
- Est ce que true est ma condition?
Et non ce n’est pas un remake de Star Wars mais bien les assertions Junit
Hamcrest
Joe Walnes lors de la mise en place du framework JMock a mis au point un nouveau mécanisme d’assertion qui a donné lieu au projet Hamcrest (http://hamcrest.org/).
Hamcrest a été intégré à Junit à partir de la version 4.4 dans sa version 1.2 et la version 1.3 a été intégré à partir de Junit 4.11
Hamcrest propose une nouvelle méthode pour vérifier les assertions
assertThat([value], [matcher statement]);
Cette syntaxe est déjà plus lisible. Si je veux verifier que i=5
- En Junit on avait “Est ce que Egal à 5 est i ?”
- Avec Hamcrest on aura “Est ce que i est égal à 5 ?”
Un avantage supplémentaire d’Hamcrest est d’amener un grand nombre de matchers et de vous proposer une manière simple pour créer les votre. Une partie des Matchers a été intégrée à Junit dans org.hamcrest.CoreMatchers et org.junit.matchers.JUnitMatchers
En utilisant des imports statiques des classes, on commence à avoir des tests plus lisibles
Framework fwk = classeATester.getById(1L); assertThat(fwk.getId(), equalTo(1L)); assertThat(fwk.getLanguage(), equalTo(ProgrammingLanguage.JAVA)); assertThat(fwk.getName(), equalTo("Junit")); assertThat(fwk.getVersion(), equalTo("4.11"));
Mais la syntaxe pourrait être encore plus claire. C’est là que rentre en jeu Fest-Assert qui propose une fluent API et un très grand nombre de matcher.
Fest Assert
Le projet Fest http://fest.easytesting.org/ est découpé en plusieurs modules offrant des fonctionnalités pour simplifier la mise en place de vos tests. Fest-swing permet par exemple d’écrire des tests fonctionnels sur des applications écrites en Swing. Nous nous intéresserons qu’au module facilitant l’écriture des assertions, fest-assert.
Pour récupérer Fest-assert dans un repo Maven voici la description de l’artefact
<dependency> <groupId>org.easytesting</groupId> <artifactId>fest-assert</artifactId> <version>1.4</version> </dependency>
Au niveau de la syntaxe si je reprends l’exemple précédent nous aurons
Framework fwk = classeATester.getById(1L); assertThat(fwk.getId()).isEqualTo(1L); assertThat(fwk.getLanguage()).isNotNull().isIn(ProgrammingLanguage.values()) .isEqualTo(ProgrammingLanguage.JAVA); assertThat(fwk.getName()).hasSize(5).isEqualTo("Junit"); assertThat(fwk.getVersion()).startsWith("4.").isEqualTo("4.11");
Avec Fest-assert une assertion se résume en une phrase composée d’un sujet, verbe et complément. Par exemple : Est ce que le nom a une taille de 5 caractères et est ce qu’il est égal à “Junit” ?
Un des avantages de fest-assert est de proposer des méthodes d’assertions différentes suivant la nature de l’objet. On trouve des asserts pour ne nombreux objets Boolean, Byte, Char, Double, Float, Integer, Long, Short, String, Throwable, File, Array, List, Image, Iterator.
Pour les listes on arrive à avoir des choses assez évoluées comme par exemple
List<Framework> fwks = classeATester.getAll(); assertThat(fwks).isNotEmpty().hasSize(3) .onProperty("name").containsExactly("Junit","TestNG","Hamcrest");
Vous pouvez bien sûr créer vos propres méthodes d’assertion. Par exemple
/** * Objet permettant de faciliter le controle d'un objet Framework */ public class FrameworkAssert extends GenericAssert<FrameworkAssert, Framework> { /** * Creates a new <code>{@link org.fest.assertions.GenericAssert}</code>. * @param actual the actual value to verify. */ protected FrameworkAssert(Framework actual) { super(FrameworkAssert.class, actual); } /** * an entry point for FrameworkAssert */ public static FrameworkAssert assertThat(Framework actual) { return new FrameworkAssert(actual); } /** * Vérifie que les champs de la clé fonctionnelle sont renseignés */ public FrameworkAssert hasUniqueKey() { Assertions.assertThat(actual.getName()).overridingErrorMessage("Name is required").isNotNull(); Assertions.assertThat(actual.getVersion()).overridingErrorMessage("Version is required").isNotNull(); Assertions.assertThat(actual.getLanguage()).overridingErrorMessage("Language is required").isNotNull(); return this; } }
Conclusion
Au final si on compare les trois, on peut voir que la syntaxe la plus claire est celle de fest-assert
// JUnit
assertEquals(expected, actual); // Hamcrest assertThat(actual, equalTo(expected)); // FEST-Assert assertThat(actual).isEqualTo(expected);Vous pouvez retrouver un projet exemple sur Github à l’adresse suivante https://github.com/javamind/annexe/tree/master/JugLyonFestAssert
Il est important d’écrire des classes de tests les plus simples possible pour faciliter leur lecture. N’hésitez pas à utiliser des commentaires et des noms explicites dans le nom de vos méthodes de tests
/** * Test de la classe {@link com.ehret.jug.facade.FrameworkFacade} */ public class AssertFestJunitTest { private IFrameworkFacade classeATester; @Before public void init(){ classeATester = new FrameworkFacade(); } /** * test de {@link com.ehret.jug.facade.IFrameworkFacade#getById(Long)} avec argument vide */ @Test public void getByIdWithIdNullShouldReturnAnException(){ try{ classeATester.getById(null); Assert.fail("Doit planter avant"); } catch (RuntimeException e){ assertThat(e).isInstanceOf(NullPointerException.class).hasMessage("Framework id is required"); } } /** * test du cas nominal {@link com.ehret.jug.facade.IFrameworkFacade#getById(Long)} */ @Test public void getByIdShouldReturnAFramework(){ Framework fwk = classeATester.getById(1L); assertThat(fwk.getId()).isEqualTo(1L); assertThat(fwk.getLanguage()).isNotNull().isIn(ProgrammingLanguage.values()) .isEqualTo(ProgrammingLanguage.JAVA); assertThat(fwk.getName()).hasSize(5).isEqualTo("Junit"); assertThat(fwk.getVersion()).startsWith("4.").isEqualTo("4.11"); FrameworkAssert.assertThat(fwk).hasUniqueKey(); } }
Aucun commentaire:
Enregistrer un commentaire
Remarque : Seul un membre de ce blog est autorisé à enregistrer un commentaire.