JMockit 101

1. Introduction

Avec cet article, nous allons commencer une nouvelle série centrée sur la boîte à outils moqueuse JMockit.

Dans ce premier épisode, nous parlerons de ce qu'est JMockit, de ses caractéristiques et de la manière dont les simulacres sont créés et utilisés avec lui.

Les articles ultérieurs se concentreront sur ses capacités et les approfondiront.

2. JMockit

2.1. introduction

Tout d'abord, parlons de ce qu'est JMockit: un framework Java pour moquer des objets dans les tests (vous pouvez l'utiliser à la fois pour JUnit et TestNG).

Il utilise les API d'instrumentation de Java pour modifier le bytecode des classes pendant l'exécution afin de modifier dynamiquement leur comportement. Certains de ses points forts sont son expressibilité et sa capacité prête à l'emploi à se moquer des méthodes statiques et privées.

Peut-être que vous êtes nouveau sur JMockit, mais ce n'est certainement pas parce qu'il est nouveau. Le développement de JMockit a commencé en juin 2006 et sa première version stable date de décembre 2012, il existe donc depuis un certain temps maintenant (la version actuelle est la 1.24 au moment de la rédaction de l'article).

2.2. Dépendance de Maven

Tout d'abord, nous devons ajouter la dépendance jmockit à notre projet:

 org.jmockit jmockit 1.41 

2.3. L'expressibilité de JMockit

Comme indiqué précédemment, l'un des points forts de JMockit est son expressibilité. Pour créer des simulations et définir leur comportement, au lieu d'appeler des méthodes à partir de l'API de simulation, il vous suffit de les définir directement.

Cela signifie que vous ne ferez pas des choses comme:

API.expect(mockInstance.method()).andThenReturn(value).times(2);

Au lieu de cela, attendez-vous à des choses comme:

new Expectation() { mockInstance.method(); result = value; times = 2; }

Il peut sembler que ce soit plus de code, mais vous pouvez simplement mettre les trois lignes sur une seule. La partie vraiment importante est que vous ne vous retrouvez pas avec un grand «train» d'appels de méthode en chaîne. Au lieu de cela, vous vous retrouvez avec une définition de la façon dont vous voulez que le simulacre se comporte lorsqu'il est appelé.

Si vous prenez en compte que sur la partie résultat = valeur, vous pouvez renvoyer n'importe quoi (valeurs fixes, valeurs générées dynamiquement, exceptions, etc.), l'expressivité de JMockit devient encore plus évidente.

2.4. Le modèle Record-Replay-Verify

Les tests utilisant JMockit sont divisés en trois étapes différenciées: enregistrer, rejouer et vérifier.

  1. Lors de la phase d' enregistrement , lors de la préparation des tests et avant les appels aux méthodes que nous voulons exécuter, nous définirons le comportement attendu pour tous les tests à utiliser lors de l'étape suivante.
  2. La phase de relecture est celle dans laquelle le code testé est exécuté. Les appels de méthodes / constructeurs simulés précédemment enregistrés à l'étape précédente seront maintenant rejoués.
  3. Enfin, sur la phase de vérification , nous affirmerons que le résultat du test était celui que nous attendions (et que les simulacres se sont comportés et ont été utilisés selon ce qui a été défini dans la phase d'enregistrement).

Avec un exemple de code, une image filaire pour un test ressemblerait à ceci:

@Test public void testWireframe() { // preparation code not specific to JMockit, if any new Expectations() {{ // define expected behaviour for mocks }}; // execute code-under-test new Verifications() {{ // verify mocks }}; // assertions }

3. Créer des simulacres

3.1. Annotations de JMockit

Lors de l'utilisation de JMockit, le moyen le plus simple d'utiliser des simulations est d'utiliser des annotations. Il y en a trois pour créer des simulations ( @Mocked , @Injectable et @Capturing ) et un pour spécifier la classe en cours de test ( @Tested ).

Lorsque vous utilisez l' annotation @Mocked sur un champ, cela crée des instances simulées de chaque nouvel objet de cette classe particulière.

D'autre part, avec l' annotation @Injectable , une seule instance simulée sera créée.

La dernière annotation, @Capturing se comportera comme @Mocked, mais étendra sa portée à chaque sous-classe étendant ou implémentant le type du champ annoté.

3.2. Passer des arguments aux tests

Lors de l'utilisation de JMockit, il est possible de passer des simulations en tant que paramètres de test. Ceci est très utile pour créer une maquette juste pour ce test en particulier, comme un objet modèle complexe qui nécessite un comportement spécifique juste pour un test par exemple. Ce serait quelque chose comme ça:

@RunWith(JMockit.class) public class TestPassingArguments { @Injectable private Foo mockForEveryTest; @Tested private Bar bar; @Test public void testExample(@Mocked Xyz mockForJustThisTest) { new Expectations() {{ mockForEveryTest.someMethod("foo"); mockForJustThisTest.someOtherMethod(); }}; bar.codeUnderTest(); } }

Cette façon de créer un simulacre en le passant en paramètre, au lieu d'avoir à appeler une méthode API, nous montre à nouveau l'expressibilité dont nous parlons depuis le début.

3.3. Exemple complet

Pour terminer cet article, nous allons inclure un exemple complet de test utilisant JMockit.

Dans cet exemple, nous allons tester une classe Performer qui utilise Collaborator dans sa méthode perform () . Cette méthode perform () reçoit un objet Model comme paramètre à partir duquel elle utilisera son getInfo () qui retourne une chaîne, cette chaîne sera transmise à la méthode collaborate () de Collaborator qui retournera true pour ce test particulier, et cette valeur sera transmise à la méthode receive () de Collaborator .

Ainsi, les classes testées ressembleront à ceci:

public class Model { public String getInfo(){ return "info"; } } public class Collaborator { public boolean collaborate(String string){ return false; } public void receive(boolean bool){ // NOOP } } public class Performer { private Collaborator collaborator; public void perform(Model model) { boolean value = collaborator.collaborate(model.getInfo()); collaborator.receive(value); } }

Et le code du test finira par être comme:

@RunWith(JMockit.class) public class PerformerTest { @Injectable private Collaborator collaborator; @Tested private Performer performer; @Test public void testThePerformMethod(@Mocked Model model) { new Expectations() {{ model.getInfo();result = "bar"; collaborator.collaborate("bar"); result = true; }}; performer.perform(model); new Verifications() {{ collaborator.receive(true); }}; } }

4. Conclusion

Avec cela, nous terminerons notre introduction pratique à JMockit. Si vous souhaitez en savoir plus sur JMockit, restez à l'écoute pour les prochains articles.

L'implémentation complète de ce tutoriel se trouve sur le projet GitHub.

4.1. Articles de la série

Tous les articles de la série:

  • JMockit 101
  • Un guide de JMockit - Attentes
  • Utilisation avancée de JMockit