Comment vérifier plusieurs appels de méthode avec différents paramètres


116

J'ai la méthode suivante sur laquelle je souhaite vérifier le comportement.

public void methodToTest(Exception e, ActionErrors errors) {
    ...

    errors.add("exception.message", 
            ActionMessageFactory.createErrorMessage(e.toString()));

    errors.add("exception.detail",
            ActionMessageFactory.createErrorMessage(e.getStackTrace()[0].toString()));

    ...
}

Dans ma classe @Test, j'espérais faire quelque chose comme ça pour vérifier que cela errors.add()est appelé avec "exception.message" et à nouveau avec "exception.detail"

verify(errors).add(eq("exception.message"), any(ActionError.class));
verify(errors).add(eq("exception.detail"), any(ActionError.class));

cependant Mockito se plaint comme suit

Argument(s) are different! Wanted:
actionErrors.add(
    "exception.message",
    <any>
);

Actual invocation has different arguments:
actionErrors.add(
    "exception.detail",
    org.apache.struts.action.ActionError@38063806
);

Comment puis-je dire à Mockito de vérifier les deux valeurs?


1
quand vous avez 2 méthodes avec une signature différente, vous pouvez écrire un cas de test séparé pour les deux.
Naveen Babu

8
Oui, mais dans ce cas, c'est la même signature de méthode mais juste des valeurs d'argument différentes
Brad

vous pourriez essayer d'utiliserMockito.reset()
takacsot

Réponses:


102

Une lecture plus approfondie m'a amené à essayer d'utiliser ArgumentCaptors et les travaux suivants, bien que beaucoup plus verbeux que je ne le souhaiterais.

ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);

verify(errors, atLeastOnce()).add(argument.capture(), any(ActionMessage.class));

List<String> values = argument.getAllValues();

assertTrue(values.contains("exception.message"));
assertTrue(values.contains("exception.detail"));

existe-t-il un moyen de s'assurer que certains paramètres ont été appariés en utilisant cette approche? Dites par exemple la méthode OP avait deux arguments et je voulais vérifier qu'ils ont été appelés ensemble
committedandroider

1
Le scénario de test de l'OP appelle methodToTest()exactement une fois, par conséquent, cette réponse vérifie que les deux appels sont effectués ensemble. La capture List<String> valuesqui est affirmée ne contiendra que les deux valeurs testées et aucune autre. Vous pouvez également ajouter assertTrue(values.size == 2). Si c'est ce que vous voulez, je remplacerais les 3 déclarations assertTrue par un seul Hamcrest ...assertThat(values, contains("exception.message", "exception.detail"));
Brad

Le cas de test d'OP n'appelle-t-il pas methodToTest () deux fois?
committedandroider

désolé je n'ai pas été clair. Je faisais référence au scénario où OP voulait tester que deux arguments étaient appelés conjointement. Ainsi, la signature de la méthode ressemblerait à quelque chose comme public void methodToTest (Exception e, Message m, ActionErrors errors) {afin qu'une exception spécifique soit appelée avec un message spécifique. Je supposé que vous pourriez avoir deux ArgumentCaptors puis récupérer l'index et comparer avec les valeurs de ces indices dans les deux listes de valeurs
committedandroider

Le cas de test d'OP appelle methodToTest()une fois. C'est l'argument de la méthode qui ActionErrors errorsest appelé deux fois en interne.
Brad

61

Si l'ordre des deux add()appels est pertinent, vous pouvez utiliser InOrder:

InOrder inOrder = inOrder(errors);
inOrder.verify(errors).add(eq("exception.message"), any(ActionError.class));
inOrder.verify(errors).add(eq("exception.detail"), any(ActionError.class));

7
Il suffit de passer un seul errorsargument: InOrder inOrder = inOrder(errors);(voir docs )
GreenhouseVeg

2
Et si la commande n'est PAS pertinente? ce qui est souvent le cas.
haelix

1
@haelix Dans ce cas, utilisez la réponse de Brad. Convertissez le Listen Setet affirmez que l'ensemble des entrées est égal à l'ensemble donné par l'argument capture.
flopshot

25

Essayez quelque chose comme ceci:

verify(errors, times(2))
     .add(AdditionalMatchers.or(eq("exception.message"), eq("exception.detail")),
          any(ActionError.class));

5
Votre chèque est évidemment trop détendu.
haelix

17

vous avez probablement un problème dans votre code. Parce qu'en fait, vous écrivez ce code:

Map<Character, String> map = mock(Map.class);

map.put('a', "a");
map.put('b', "b");
map.put('c', "c");

verify(map).put(eq('c'), anyString());
verify(map).put(eq('a'), anyString());
verify(map).put(eq('b'), anyString());

Notez que la première vérification n'est même pas dans l'ordre en ce qui concerne les invocations réelles.

De plus, je vous recommande de ne pas vous moquer des types que vous ne possédez pas, par exemple le type struts.

[MODIFIER @Brad]

Après avoir exécuté le code de Brice (ci-dessus) dans mon IDE, je peux voir que j'ai utilisé ActionError au lieu d'ActionMessage, c'est pourquoi mon verify () ne correspondait pas. Le message d'erreur que j'ai initialement publié m'a induit en erreur en pensant que c'était le premier argument qui ne correspondait pas. Il s'avère que c'était le deuxième argument.

Donc la réponse à ma question est

/** 
 * note that ActionMessageFactory.createErrorMessage() returns ActionMessage
 * and ActionError extends ActionMessage
 */
verify(errors).add(eq("exception.message"), any(ActionMessage.class));
verify(errors).add(eq("exception.detail"), any(ActionMessage.class));

1
Ne comprenez pas ce que vous essayez de dire. L'ordre de vérification est-il important? si l'ordre de vérification est important. Pourquoi alors l'API InOrder est-elle fournie?
Oleksandr Papchenko

Tout comme ce qui est écrit ci-dessus, l'ordre de vérification n'est pas pertinent; c'est pourquoi il y a InOrder.
Brice

12

Vous pouvez utiliser Mockito.atLeastOnce()ce qui permet à Mockito de passer le test même si ce mockObject sera appelé plusieurs fois.

Mockito.verify(mockObject, Mockito.atLeastOnce()).testMethod(Mockito.eq(1));

Mockito.verify(mockObject, Mockito.atLeastOnce()).testMethod(Mockito.eq(2));

1

1) Dites à Mokito l'attente totale des appels.

2) Dites à Mokito combien de fois chaque combinaison de paramètres était attendue.

verify(errors, times(2)).add(any(), any(ActionMessage.class));

verify(errors, atLeastOnce()).add(eq("exception.message"), any());
verify(errors, atLeastOnce()).add(eq("exception.detail"), any());

0

De la même manière que @ sendon1928, nous pouvons utiliser:

Mockito.times(wantedInvocationCount)

pour s'assurer que la méthode a été appelée le nombre exact de fois (solution préférable à mon avis). Ensuite, nous pouvons appeler

Mockito.verifyNoMoreInteractions(mock)

Pour s'assurer que ce simulacre n'est plus utilisé dans aucun contexte. Exemple complet:

Mockito.verify(mockObject, Mockito.times(wantedInvocationCount)).testMethod(Mockito.eq(1));

Mockito.verify(mockObject, Mockito.times(wantedInvocationCount)).testMethod(Mockito.eq(2));

Mockito.verifyNoMoreInteractions(mockObject)
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.