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?

Brad
la source
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"));
Brad
la source
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));
Christoph Walesch
la source
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));
John B
la source
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));
Brice
la source
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));
sendon1982
la source
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());
épox
la source
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)
Michał Powłoka
la source