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 et Mockito,
  • 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

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Sapient, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.