Il y a 13 ans -
Temps de lecture 1 minute
Devoxx – Jour 3 – ScalaTest
Deuxième partie de nos billets Devoxx consacrés à Scala. Après les acteurs, c’est au tour de la librairie ScalaTest de faire son show.
Au cours de cette présentation, nous verrons les fonctionnalités de cette librairie, son intégration avec les outils actuels du marché (comme JUnit ou TestNG) mais aussi l’intérieur de la bête. Cela nous permettra de mettre les mains dans ScalaTest ! Le mot d’ordre de la session : productivité !
Présentation
ScalaTest est un framework open-source de tests unitaires pour la plateforme Java. Il est possible de tester à la fois des classes Java mais aussi des classes Scala. L’objectif est d’augmenter la productivité du développeur qui écrira moins de lignes de test; Ces lignes étant plus claires que si elles avaient été écrites en Java. Moins de lignes, plus claires que du Java ? Je veux des exemples…
Le Trait principal de l’API est Suite
qui fournit une multitude de méthodes tests et utilitaires comme run
, runNestedSuites
ou bien encore withFixtures
(les détails des méthodes et de ce qu’elles font se trouvent sur la scaladoc du projet). De là découlent des classes concrètes comme JUnitSuite
, JUnit3Suite
, JUnitWrapperSuite
, TestNGSuite
ou TestNGWrapperSuite
. Plusieurs Matchers
nous permettrons d’écrire nos tests avec des facilités d’écriture (DSLs). On retiendra AssertionsForJUnit
, ShouldMatchersForJUnit
(qui donne vraiment l’impression que l’on écrit notre test comme on écrirait des phrases en anglais) mais aussi MustMatchersForJUnit
.
Petite comparaison
Le premier exemple nous montre les nuances entre différentes APIs de tests unitaires dont JUnit 3 et JUnit 4 et avec ScalaTest :
String s = "hi" ; // JUnit 3 try { s.charAt(-1); fail(); } catch (StringIndexOutOfBoundException e) { // Expected, so continue } // JUnit 4 @Test(expected= StringIndexOutOfBoundException.class) public void testPassingANegativeToCharAt() { s.charAt(-1); } // ScalaTest intercept[StringIndexOutOfBoundException] { s.charAt(-1) }
Les non-habitués auront l’impression de manipuler une syntaxe du langage comme le synchronized
Java (qui a la même écriture), alors qu’en fait il ne s’agit que d’un appel à une méthode qui prend en entrée une fonction à exécuter. La méthode intercept
exécute tout de suite la fonction en paramètre et catch
une éventuelle exception. Celle-ci sera comparée au type d’exception attendu (ici StringIndexOutOfBoundException
). Le plus gros du travail aura été de récupérer le type de (T<:Any)
de intercept
afin de comparer la classe de l'exception levée à celle attendue (T
). Pour cela, les développeurs ont utilisé la classe Scala scala.reflect.Manifest
qui permet d'accéder à l'erasure du type T
.
Le ton est donné, nous sommes dans cette salle pour apprendre à utiliser la librairie mais aussi pour mettre les mains dans ScalaTest et voir l'implémentation de quelques fonctionnalités de l'outil (intercept, mock...). En gros, des conférenciers les yeux rivés sur les slides et heureux d'être là !
Les mocks
Dans ScalaTest, les mocks ne sont pas en reste. Pour les utilisateurs d'EasyMock, ScalaTest propose une autre manière d'écrire ces mocks avec un wrapping des appels à replay
et verify
. Encore une fois, cela passe par une méthode qui prendra en entrée une fonction et fera l'appel à replay
ou verify
à la fin de l'exécution de la fonction :
// Wrap replay expecting { mockCollaborator.documentAdded("document") } // Wrap verify whenExecuting(mockCollaborator) { classUnderTest.addDocument("document", new Array[Byte](0)) }
You should do that !
Bill enchaîne ensuite avec le matcher should et l'exemple nous montrant que ce n'est pas de la magie mais juste une bonne utilisation des sucres syntaxiques de Scala :
map should contain key 'a' // Equivalent to map.should(contain).key('a')
Ainsi, en écrivant l'API d'une certaine manière, on obtient des méthodes qui peuvent être appelées sans point ni parenthèses. D'autres exemples nous sont donnés :
"A Stack (when empty)" should "be empty" in { emptyStack should not be "empty" // This should fail } it should "complain on peek" in { evaluating( emptyStack.peek ) should produce [IllegalStateException] } ignore should "complain on pop" in { evaluating( emptyStack.pop ) should produce [IllegalStateException] } myInt should be > 0 myOtherInt should be === 0
GivenWithThen
Dernière subtilité concernant ScalaTest avec le Trait GivenWithThen
. Celui-ci permet de documenter les tests mais, à la différence d'un commentaire dans le code, ceux-ci apparaîtront dans le rapport d'exécution. Exemple avec le résultat à l'exécution en image live :) :
when("pop is invoked on the stack") var result = stack.pop() then("the most recently pushed element should be returned") assert(result === 2) and("the stack should have one less item than before") assert(stack.getSize === oldSize - 1)

Conclusion
Avec cette version 1.0, nous retiendrons :
- Une meilleure intégration avec
JUnit
, - Une meilleure intégration avec
JMock
,EasyMock
etMockito
, - Le trait
GivenWithThen
, - Plusieurs nouvelles
FixtureSuite
qui facilitent la programmation fonctionnelle et l'injection de fixtures directement dans les tests.
ScalaTest 1.0 se télécharge sur cette page, soit directement le fichier ZIP soit la dépendance Maven.
Commentaire