JMockit 101

JMockit 101

1. introduction

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

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 suivants se concentreront et approfondiront ses capacités.

2. JMockit

2.1. introduction

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

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é hors du commun à 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, donc il existe depuis un certain temps maintenant (la version actuelle est la 1.24 au moment de la rédaction de l'article).

2.2. L'expressibilité de JMockit

Comme indiqué précédemment, l'un des points forts de JMockit est son expressibilité. Afin de créer des simulacres et de définir leur comportement, au lieu d'appeler des méthodes à partir de l'API simulatrice, 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 c'est 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 la maquette se comporte lorsqu'elle est appelée.

Si vous prenez en compte que sur la partieresult = value, 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.3. Le modèle Record-Replay-Verify

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

  1. Sur la phaserecord, 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 la prochaine étape.

  2. La phasereplay est celle dans laquelle le code sous test est exécuté. Les invocations de méthodes / constructeurs fictifs précédemment enregistrés sur l’étape précédente vont maintenant être rejouées.

  3. Enfin, sur la phaseverify, nous affirmerons que le résultat du test était celui que nous attendions (et que les mocks 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 structure 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

Lorsque vous utilisez JMockit, le moyen le plus simple d’utiliser des simulacres consiste à utiliser des annotations. Il y en a trois pour créer des simulations (@Mocked,@Injectable et@Capturing) et un pour spécifier la classe testée (@Tested).

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

Par contre, 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 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 une maquette en la passant en paramètre, au lieu d’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 classePerformer qui utiliseCollaborator dans sa méthodeperform(). Cette méthodeperform(), reçoit un objetModel comme paramètre à partir duquel elle utilisera sesgetInfo() qui retourne une chaîne, cette chaîne sera passée à la méthodecollaborate() deCollaborator qui renverratrue pour ce test particulier, et cette valeur sera transmise à la méthodereceive() à partir deCollaborator.

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 peut être trouvée surthe GitHub project.

4.1. Articles de la série

Tous les articles de la série: