Voici enfin le dernier billet de ma visite à Jazoon 2009. Je termine le troisième jour par une présentation de Mockito par l’excellent Szczepan Faber. Il bosse à Varsovie en Pologne, c’est un ancien de ThoughWorks London, un ancien barman lorsqu’il était étudiant, et surtout ce genre de personne qui vous font passer une heure délirante en parlant d’un sujet sérieux.
Allez soyons sérieux 30 secondes…
Après une introduction, sa présentation débute par un slide avec la définition d’un Mock.
Il pose la question : qu’est-ce qu’un mock ?
Sa réponse : « C’est un substitut d’un objet réel afin de faire des tests, en simulant les interactions« .
Comme personne ne pipe mot, il appuie sur espace et une… poupée gonflable apparaît alors : « vous voyez ? un mock permet de se passer de la vraie instance d’un objet, afin de pouvoir faire des tests tranquillement avec un substitut…« .
La salle est morte de rires, je crois que tout le monde se souviendra de la définition d’un Mock.
Il continue ensuite, et puis au 4ème slide il démarre Eclipse, afin de nous montrer un peu comment marche Mockito. Hop quelques lignes, on note au passage sa maîtrise des raccourcis d’Eclipse, parfois j’ai même eu envie de passer à Eclipse et de laisser tomber IDEA IntelliJ. Mais vu que visiblement ce bonhomme n’est pas humain, ou qu’il y a un truc, je suis donc resté à intelliJ.
Le voilà qui code une classe, un mock. Mais il utilise… EasyMock. Est-ce que quelqu’un va lui dire qu’il s’est planté ? Que nenni ! C’est fait exprès…. Aaaah d’accord. Là je vais pas pouvoir continuer à bloguer cher lecteur, car le bonhomme en question a ensuite passé 32 minutes à coder, comme une bête.
J’ai tout d’abord retenu une petite astuce toute simple à reprendre dans vos tests unitaires. Utilisez les expressions « given/when/then » pour exprimer vos tests.
Cela donne par exemple :
@Test public void shouldIncrementMyValue() throws Exception { // given // when // then }
J’ai ensuite surtout vu la différence entre easyMock et Mockito. Respect à David Gageot qui nous l’a dit il y a 6 mois. Oui Mockito est mieux qu’easyMock. Maintenant il faut que je dise pourquoi. Ca c’est le plus dur, surtout sans notes. Durant sa présentation je n’ai pas eu le temps de prendre des notes. Ce bonhomme étant trop fort avec machin-eclipse, impossible de le suivre. Ensuite, sa présentation étant vraiment bien, et sans temps morts, j’ai pas pris de notes. Voilà désolé, hop là je sens un grand cri de frustration… allez je vais chercher des bouts de codes et je reviens. Mais je vous dis, sans notes, ça va être difficile.
Regardons tout d’abord Mockito, le code ci-dessous n’est pas du bon code mais simplement quelques lignes afin de vous montrer le fonctionnement de Mockito.
import static org.mockito.Mockito.*; import java.util.LinkedList; /** * Simple Mockito test. * @author Nicolas Martignole */ public class Test { @SuppressWarnings("unchecked") public static void main(String[] args) { // Creation d'un mock LinkedList mockedList = mock(LinkedList.class); mockedList.add("Test Touilleur 1"); mockedList.clear(); // Faire autre chose // .... // Verifier que l'on a ajoute un element verify(mockedList).add("Test Touilleur 1"); // Verifier que l'on a vide la liste verify(mockedList).clear(); } }
La différence avec EasyMock : pas de replay à effectuer. Le premier appel à un verify bascule le mock automatiquement. On voit aussi que la syntaxe est facilement lisible : « verifier que mockedList point clear a ete appelé »
Mockito est aussi très intéressant lorsqu’un événement non attendu survient. Imaginons que je m’attende à ce que ma liste « mockée » soit manipulée, que l’on effectue un deuxième ajout. J’ajoute donc une étape de vérification qui va planter puisque je n’ai pas ajouté de deuxième élément à ma liste. Regardons comment le code réagit dans ce cas précis :
import static org.mockito.Mockito.*; import java.util.LinkedList; /** * Simple Mockito test. * @author Nicolas Martignole */ public class Test { @SuppressWarnings("unchecked") public static void main(String[] args) { // Creation d'un mock LinkedList mockedList = mock(LinkedList.class); mockedList.add("Test Touilleur 1"); mockedList.clear(); // Faire autre chose // Verifier que l'on a ajoute un element verify(mockedList).add("Test Touilleur 1"); // Verifier que l'on a vide la liste verify(mockedList).clear(); // Verifier que l'on a aussi ajouter un autre element // ce qui n'est pas le cas, Mockito va donc // nous le signaler verify(mockedList).add("not added"); } }
Voici ce que Mockito affiche lors de l’exécution :
Exception in thread "main" org.mockito.exceptions.verification.WantedButNotInvoked: Wanted but not invoked: linkedList.add("not added"); at Test.main(Test.java:30)
En clair : vous avez demandé à vérifier que l’on ajoute la phrase « not added » mais ce n’est pas le cas. Mockito produit des exceptions très claires lorsqu’un cas non attendu survient. Il sépare clairement aussi la définition du stub de la partie vérification, très facilement.
Je vous encourage à tester Mockito si vous utilisez easyMock, ou si les Mocks ne vous disent rien. Mockito permet de mocker des classes concrètes, certain préfèrent mettre des interfaces partout, et mocker ces interfaces. A la limite, faites ce qui vous arrange, la pratique des interfaces venant d’un livre de Martin Fowler qui disait, je crois, qu’il faut mocker des rôles, donc des interfaces.
Quelques derniers mots sur les charmes de Mockito :
– possibilité d’utiliser une vérification avec un ordre : inOrder()
– atLeast(x), times(x), etc.
– verifyNoMoreInteractions()
– enchainement de commandes: thenReturn(x).thenThrow(y)
– possibilité de matcher avec des arguments comme: anyObject(), etc.
– utilise hamcrest comme jMock
J’espère que nous aurons l’occasion de rencontrer Szczepan à Paris, au Paris JUG. Si vous le voyez à Devoxx 2009, croyez-moi cela vaut le coup.
Références
– Blog de Szczepan
– http://mockito.org/
– L’api de Mockito
Retrouvez l’ensemble des mes articles sur Jazoon 2009 sur cette page
Il me semble aussi qu’utiliser des interfaces vient aussi de Spring qui a mis l’accent sur la séparation entre le contrat (l’interface) et l’implémentation. Justement en mettant en avant qu’on pouvait même avec Spring remplacer une implémentation par un mock.
Juste une petite remarque sur l’utilisation de Mockito. Plutôt que d’utiliser les verify(), on aura plutôt tendance à utiliser les when(). C’est à dire que plutôt que de tester si un mock a bien été appelé, on le fait réagir (when) dans un contexte donné (given) et on vérifie que le résultat renvoyé par l’appel à l’objet testé est bien celui attendu (then). C’est une petite nuance qui change tout. C’est ce qui fait aussi le charme de Mockito.
Petite remarque hors sujet pour relancer le débat IntelliJ/Eclipse puisque l’article m’y a fait penser. Lorsque j’ai commencé sérieusement à devoir utiliser Eclipse (projet de 12000 classes), j’ai imprimé la liste des raccourcis clavier et je l’ai apprise par coeur. Je ne me souviens pas d etout mais une bonne partie quand même. Ensuite, pour mieux comprendre la philosophie du bouzou, je me suis mis à lire un livre (Eclipse 3.0 pour les développeurs si je me souviens) et puis j’ai publié mon premier plugin Eclipse. Et depuis, je n’arrive pas à sortir d’Eclipse. Je râle, je peste parce que je rencontre pas mal de bugs à l’utilisation dans la multitude de plugins. Et si régulièrement, je teste un autre IDE (j’ai une licence Open Source IntelliJ), je reviens tjrs à Eclipse. En fait, cette connaissance des raccourcis clavier m’epmpêche finalement d’aller voir aiileurs. D’autant plus que ceux d’IntelliJ ne sont top moumoute et rentrent en conflit avec les raccourcis d’Ubuntu.