Mix-IT est une conférence co-organisée par le Lyon JUG et le Cara (Club Agile Rhône-Alpes) qui s’est déroulée le 5 avril dernier. En réunissant la communauté Jave et la communauté Agile, ce fut l’occasion d’assister à des conférences sur ces 2 thèmes. 250 participants, une très bonne organisation grâce aux étudiants de l’EPITECH de Lyon, bravo à toute l’équipe.
Je débute la journée par une présentation de Mathilde Lemée, sur le framework Spock. Mathilde est indépendante, co-auteur du blog Java-Freelance.fr avec Jean-Baptise, son mari, indépendant lui aussi. Ils font aussi partie du réseau des Zindeps, comme moi. Elle fait aussi partie de l’équipe d’organisation des JDuchess.
Spock Framework est un framework open-source destiné à rendre plus clair l’écriture des tests unitaires et des tests fonctionnels. Ecrit en Groovy, il tourne parfaitement dans votre univers habituel grâce à un runner JUnit. Vous pouvez donc écrire des tests en Groovy, afin de tester votre code Java, et faire fonctionner le tout dans votre IDE favori.
Ecrire un bon test, c’est presque plus compliqué qu’écrire un bon code métier. Robert C. Martin (dit « Uncle Bob ») a d’ailleurs énoncé quelques regles suivantes dans son livre « Clean Code » :
– Vous n’écrirez pas de code métier sans écrire de code de test avant
– Vous ne devez pas écrire de code métier tant que le code de votre test n’échoue pas d’abord.
– Vous ne pouvez écrire qu’un seul test unitaire qui ne compile pas, puis écrire le code métier pour avancer. Si vous écrivez plusieurs tests, ce n’est pas bon.
– Vous ne devez écrire que du code métier suffisant pour faire passer le test
– Vous devez effacer du code métier qui n’est pas testé
Facile à apprendre, Spock permet de simplifier l’écriture des tests. Il est écrit en Groovy, et comme Mathilde l’explique bien, un Runner JUnit permet de l’utiliser sur son projet classique sans soucis.
Elle montre l’exemple classique d’un test d’une Stack:
class TestStack extends Specification { def "stack should push"() { given: def stack = new Stack(); def elem="push me" when: stack.push(elem) then: !stack.empty stack.size() == 1 stack.peek() == elem } }
Comme le montre l’exemple ci-dessus, l’un des premiers avantages de Spock est de rendre plus clair l’expression et l’écriture de vos tests. Apprendre Spock Framework ne demande pas d’effort. Un tableau résume la correspondance avec JUnit :
Spock | JUnit |
Specification | Test class |
setup() | @Before |
cleanup() | @After |
setupSpec() | @BeforeClass |
cleanupSpec() | @AfterClass |
Feature | Test |
Parameterized feature | Theory |
Condition | Assertion |
Exception condition | @Test(expected=...) |
@FailsWith | @Test(expected=...) |
Interaction | Mock expectation (EasyMock, JMock, …) |
Spock permet ensuite comme EasyMock ou Mockito d’écrire des Stubs et des Mocks. Mathilde explique la différence entre ces 2 concepts : un Stub est destiné à tester un changement d’état, alors qu’un Mock est intelligent, et vise à tester un comportement. Martin Fowler dans « Mocks aren’t Stubs » explique ainsi qu’un Mock vise à valider qu’un certain nombre de méthodes et donc de comportement est appelé. Cela introduit un couplage fort entre les tests et le code métier, puisque le code de votre test décrit les appels de méthode et les tests. EasyMock est l’un des frameworks les plus utilisés dans le monde Java pour écrire ce type de test. C’est un moyen pratique de tester que les méthodes d’un objet ont été appelées dans un certain ordre, avec des paramètres attendus.
Les stubs sont plus destinés à effectuer des tests d’état, avec des objets simples et stupides.
– Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.
– Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example).
– Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what’s programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it ‘sent’, or maybe only how many messages it ‘sent’.
– Mocks are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive.
Voici comment Spock permet d’écrire un stub :
given: securityService.getManager("user2")>>new User(789,"John","Doe") when: User manager=bigService.getManager("user2") then: manager.id==789
La première ligne avec given permet simplement de dire : « lorsque la méthode getManager sera appelée sur le service securityService avec comme argument [user2] ALORS retourne un nouvel User avec comme id 789 »
La deuxième ligne permet de tester que bigService (qui utilise entre autre securityService) appelle correctement securityService et retourne le bon utilisateur. Le test valide que l’id retourné est identique. On valide donc que bigService appelle l’objet securityService en passant l’argument, et qu’il retourne le résultat.
Mathilde explique ensuite qu’il est possible d’utiliser plusieurs méthodes pour décrire la partie interaction avec le mock :
Interaction: ----------- mock.receive() mock.receive(_) mock.receive() mock.receive(!null) mock.receive(event) mock.receive(!event)
Vous pouvez par exemple demander à Spock de vérifier qu’une méthode est au moins appelé une fois :
def "getManager should blablab"() { when: bigService.getManager "user1" then: 1*securityService.getManager (_) // methode appelee exactement une fois }
Voici un exemple de mock:
public Boolean auditUser(String username){ User u=dummyData.get(username); return securityService.hasAccess(u); } when: service.auditByUser("user1") then: 1*securityService.hasAccess(it.id=123456)
Spock permet aussi comme JUnit de spécifier qu’une méthode doit lever une exception attendue. J’ai trouvé que l’expressivité était moins intéressante lorsque Mathilde a présenté son exemple :
@Test(expected = RuntimeException.class) public void doSomething(){ when(userService.getManager("user")).thenThrow(new RuntimeException()); } securityService.getManager("user2")>>{throw new RuntimeException()} when: service.getManager("user2") then: throw new RuntimeException();
Un autre point fort et beaucoup plus intéressant, est la partie « Data Driven Testing » de Spock. Comme FIT, vous pouvez décrire des tableaux de données afin de passer non pas un, mais plusieurs séries de données à une méthode. Imaginons que nous souhaitons tester la méthode String.length(), voici comment écrire un test qui va prendre une série de données :
class HelloSpock extends spock.lang.Specification { def "test la methode length"() { expect: name.length() == taille where: name | taille "Spock" | 5 "Kirk" | 4 "Scotty" | 6 } }
Un autre exemple tiré du site Spock :
def "computing the maximum of two numbers"() { expect: Math.max(a, b) == theMax where: a << [5, 3] b << [1, 9] theMax << [5, 9] }
Mathilde explique ensuite une autre spécificité de Spock. Lorsqu’un test plante, il est parfois compliqué de comprendre ce qui s’est passé. Spock, grâce à Groovy, est capable de remonter plus d’informations afin de comprendre la raison pour laquelle un test échoue :
@Unroll("maximum of #a and #b is #c") def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a | b | c 3 | 7 | 7 5 | 4 | 5 9 | 9 | 9 }
L’annotation Unroll sera utile lorsque vous utiliserez des DataTables, afin de détecter et comprendre plus facilement ce que fait le test.
Mathilde termine par une démonstration comparative de JUnit et de Spock, qui achève de conquérir la salle. Très bonne présentation, qui donne envie au moins de tester. Le code est parfois un peu difficile à suivre, mais les concepts sont bien exposés.
Pour tester Spock Framework :
– Java-Freelance, le site de Mathilde et Jean-Baptiste
– Mocks Aren’t Stubs par M.Fowler
– EasyMock
– Mockito que je préfère à EasyMock
– un article du Touilleur Express de juin 2009 sur Mockito
– @Unroll et DataTable par Hamlet D’Arcy sur JavaLobby
Salut,
Merci pour ce retour. Adepte inconditionnel des TU et du TDD, moi aussi je préfère Mockito à Easymock, mais Spock m’était encore inconnu, à tester de toute urgence !
Xavier
Mix-it rulezzzz!! Merci Nicolas d’être venu!