Introduction à PowerMock

Introduction à PowerMock

1. Vue d'ensemble

Les tests unitaires à l'aide d'un cadre moqueur sont reconnus comme une pratique utile depuis longtemps, et le cadreMockito, en particulier, a dominé ce marché ces dernières années.

Et afin de faciliter la conception de codes décents et de simplifier l'API publique, certaines fonctionnalités souhaitées ont été volontairement omises. Dans certains cas, toutefois, ces lacunes obligent les testeurs à écrire un code compliqué simplement pour permettre la création de simulacres.

C'est là que le frameworkPowerMock entre en jeu.

PowerMockito est une API d'extension de PowerMock pour prendre en charge Mockito. Il offre des fonctionnalités permettant d'utiliser l'API Java Reflection de manière simple pour résoudre les problèmes de Mockito, tels que le manque de capacité à simuler des méthodes finales, statiques ou privées.

Ce didacticiel présentera l’API de PowerMockito et son application aux tests.

2. Préparation aux tests avec PowerMockito

La première étape pour intégrer le support PowerMock pour Mockito consiste à inclure les deux dépendances suivantes dans le fichier Maven POM:


    org.powermock
    powermock-module-junit4
    1.6.4
    test


    org.powermock
    powermock-api-mockito
    1.6.4
    test

Ensuite, nous devons préparer nos cas de test pour travailler avecPowerMockito en appliquant les deux annotations suivantes:

@RunWith(PowerMockRunner.class)
@PrepareForTest(fullyQualifiedNames = "com.example.powermockito.introduction.*")

L'élémentfullyQualifiedNames dans l'annotation@PrepareForTest représente un tableau de noms complets de types que nous voulons simuler. Dans ce cas, nous utilisons un nom de package avec un caractère générique pour dire àPowerMockito de préparer tous les types du packagecom.example.powermockito.introduction pour la simulation.

Nous sommes maintenant prêts à exploiter la puissance dePowerMockito.

3. Constructeurs moqueurs et méthodes finales

Dans cette section, nous montrerons les moyens d'obtenir une instance fictive au lieu d'une instance réelle lors de l'instanciation d'une classe avec l'opérateurnew, puis utiliserons cet objet pour simuler une méthode finale. La classe de collaboration, dont les constructeurs et les méthodes finales seront moqués, est définie comme suit:

public class CollaboratorWithFinalMethods {
    public final String helloMethod() {
        return "Hello World!";
    }
}

Tout d'abord, nous créons un objet fictif à l'aide de l'APIPowerMockito:

CollaboratorWithFinalMethods mock = mock(CollaboratorWithFinalMethods.class);

Ensuite, définissez une attente indiquant que, chaque fois que le constructeur no-arg de cette classe est appelé, une instance fictive doit être renvoyée plutôt que réelle:

whenNew(CollaboratorWithFinalMethods.class).withNoArguments().thenReturn(mock);

Voyons comment cette simulation de construction fonctionne en action en instanciant la classeCollaboratorWithFinalMethods à l'aide de son constructeur par défaut, puis vérifiez les comportements de PowerMock:

CollaboratorWithFinalMethods collaborator = new CollaboratorWithFinalMethods();
verifyNew(CollaboratorWithFinalMethods.class).withNoArguments();

Dans l'étape suivante, une attente est définie pour la méthode finale:

when(collaborator.helloMethod()).thenReturn("Hello example!");

Cette méthode est ensuite exécutée:

String welcome = collaborator.helloMethod();

Les assertions suivantes confirment que la méthodehelloMethod a été appelée sur l'objetcollaborator et renvoie la valeur définie par l'attente moqueuse:

Mockito.verify(collaborator).helloMethod();
assertEquals("Hello example!", welcome);

Si nous voulons nous moquer d'une méthode finale spécifique plutôt que de toutes les dernières à l'intérieur d'un objet, la méthodeMockito.spy(T object) peut être utile. Ceci est illustré dans la section 5.

4. Mocking Static Methods

Supposons que l'on veuille se moquer des méthodes statiques d'une classe nomméeCollaboratorWithStaticMethods. Cette classe est déclarée comme suit:

public class CollaboratorWithStaticMethods {
    public static String firstMethod(String name) {
        return "Hello " + name + " !";
    }

    public static String secondMethod() {
        return "Hello no one!";
    }

    public static String thirdMethod() {
        return "Hello no one again!";
    }
}

Afin de nous moquer de ces méthodes statiques, nous devons enregistrer la classe englobante avec l'APIPowerMockito:

mockStatic(CollaboratorWithStaticMethods.class);

Alternativement, nous pouvons utiliser la méthodeMockito.spy(Class<T> class) pour simuler une méthode spécifique, comme illustré dans la section suivante.

Ensuite, les attentes peuvent être définies pour définir les valeurs que les méthodes doivent renvoyer lorsqu'elles sont appelées:

when(CollaboratorWithStaticMethods.firstMethod(Mockito.anyString()))
  .thenReturn("Hello example!");
when(CollaboratorWithStaticMethods.secondMethod()).thenReturn("Nothing special");

Ou une exception peut être définie pour être levée lors de l'appel de la méthodethirdMethod:

doThrow(new RuntimeException()).when(CollaboratorWithStaticMethods.class);
CollaboratorWithStaticMethods.thirdMethod();

Le moment est venu d’exécuter les deux premières méthodes:

String firstWelcome = CollaboratorWithStaticMethods.firstMethod("Whoever");
String secondWelcome = CollaboratorWithStaticMethods.firstMethod("Whatever");

Au lieu d'appeler des membres de la classe réelle, les appels ci-dessus sont délégués aux méthodes de la simulation. Les affirmations suivantes prouvent que la maquette est entrée en vigueur:

assertEquals("Hello example!", firstWelcome);
assertEquals("Hello example!", secondWelcome);

Nous sommes également en mesure de vérifier les comportements des méthodes de simulation, y compris le nombre de fois qu'une méthode est invoquée. Dans ce cas, lefirstMethod a été appelé deux fois, tandis que lesecondMethod n'a jamais:

verifyStatic(Mockito.times(2));
CollaboratorWithStaticMethods.firstMethod(Mockito.anyString());

verifyStatic(Mockito.never());
CollaboratorWithStaticMethods.secondMethod();

Note: La méthodeverifyStatic doit être appelée juste avant toute vérification de méthode statique pourPowerMockito pour savoir que l'invocation de méthode successive est ce qui doit être vérifié.

Enfin, la méthode statiquethirdMethod doit lancer unRuntimeException comme déclaré précédemment sur le simulateur. Il est validé par l'élémentexpected de l'annotation@Test:

@Test(expected = RuntimeException.class)
public void givenStaticMethods_whenUsingPowerMockito_thenCorrect() {
    // other methods

    CollaboratorWithStaticMethods.thirdMethod();
}

5. Mocking partiel

Au lieu de se moquer d'une classe entière, l'APIPowerMockito permet de se moquer d'une partie de celle-ci en utilisant la méthodespy. La classe suivante sera utilisée en tant que collaborateur pour illustrer la prise en charge par PowerMock des moquages ​​partiels:

public class CollaboratorForPartialMocking {
    public static String staticMethod() {
        return "Hello example!";
    }

    public final String finalMethod() {
        return "Hello example!";
    }

    private String privateMethod() {
        return "Hello example!";
    }

    public String privateMethodCaller() {
        return privateMethod() + " Welcome to the Java world.";
    }
}

Commençons par nous moquer d'une méthode statique, nomméestaticMethod dans la définition de classe ci-dessus. Tout d'abord, utilisez l'APIPowerMockito pour simuler partiellement la classeCollaboratorForPartialMocking et définir une attente pour sa méthode statique:

spy(CollaboratorForPartialMocking.class);
when(CollaboratorForPartialMocking.staticMethod()).thenReturn("I am a static mock method.");

La méthode statique est ensuite exécutée:

returnValue = CollaboratorForPartialMocking.staticMethod();

Le comportement moqueur est vérifié comme suit:

verifyStatic();
CollaboratorForPartialMocking.staticMethod();

L'assertion suivante confirme que la méthode fictive a bien été appelée en comparant la valeur de retour à l'attente:

assertEquals("I am a static mock method.", returnValue);

Il est maintenant temps de passer aux méthodes finale et privée. Afin d'illustrer la simulation partielle de ces méthodes, nous devons instancier la classe et dire à l'APIPowerMockito àspy it:

CollaboratorForPartialMocking collaborator = new CollaboratorForPartialMocking();
CollaboratorForPartialMocking mock = spy(collaborator);

Les objets créés ci-dessus sont utilisés pour démontrer les moqueries des méthodes finale et privée. Nous allons maintenant traiter de la méthode finale en définissant une attente et en invoquant la méthode:

when(mock.finalMethod()).thenReturn("I am a final mock method.");
returnValue = mock.finalMethod();

Le comportement de se moquer partiellement de cette méthode est prouvé:

Mockito.verify(mock).finalMethod();

Un test vérifie que l'appel de la méthodefinalMethod renverra une valeur qui correspond à l'attente:

assertEquals("I am a final mock method.", returnValue);

Un processus similaire est appliqué à la méthode privée. La principale différence est que nous ne pouvons pas invoquer directement cette méthode à partir du scénario de test. Fondamentalement, une méthode privée doit être appelée par d'autres méthodes de la même classe. Dans la classeCollaboratorForPartialMocking, la méthodeprivateMethod doit être invoquée par la méthodeprivateMethodCaller et nous utiliserons cette dernière comme délégué. Commençons par l'attente et l'invocation:

when(mock, "privateMethod").thenReturn("I am a private mock method.");
returnValue = mock.privateMethodCaller();

Le moquage de la méthode privée est confirmé:

verifyPrivate(mock).invoke("privateMethod");

Le test suivant vérifie que la valeur renvoyée par l'appel de la méthode privée est identique à l'attente:

assertEquals("I am a private mock method. Welcome to the Java world.", returnValue);

6. Conclusion

Ce tutoriel a fourni une introduction à l'APIPowerMockito, démontrant son utilisation pour résoudre certains des problèmes rencontrés par les développeurs lors de l'utilisation du framework Mockito.

L'implémentation de ces exemples et extraits de code se trouve dansthe linked GitHub project.