Come verificare più chiamate di metodo con parametri diversi


116

Ho il seguente metodo su cui desidero verificare il comportamento.

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()));

    ...
}

Nella mia classe @Test speravo di fare qualcosa di simile per verificare che errors.add()venga chiamato con "exception.message" e di nuovo con "exception.detail"

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

tuttavia Mockito si lamenta come segue

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
);

Come posso dire a Mockito di controllare entrambi i valori?


1
quando hai 2 metodi con una firma diversa, puoi scrivere un test case separato per entrambi.
Naveen Babu

8
Sì, ma in questo caso è la stessa firma del metodo ma solo valori di argomento diversi
Brad

potresti provare a usareMockito.reset()
takacsot

Risposte:


102

Ulteriori letture mi hanno portato a provare a usare ArgumentCaptors e le seguenti opere, anche se molto più prolisse di quanto vorrei.

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"));

c'è un modo per assicurarsi che determinati parametri siano stati accoppiati utilizzando questo approccio? Supponiamo, ad esempio, che il metodo dell'OP avesse due argomenti e volesse verificare che fossero stati chiamati insieme
committedand

1
Il caso di test dell'OP chiama methodToTest()esattamente una volta, quindi questa risposta verifica che le due chiamate siano effettuate insieme. L'acquisizione List<String> valuesche viene affermata conterrà solo i due valori testati e nessun altro. Potresti anche aggiungere assertTrue(values.size == 2). Se questo è ciò che vuoi, sostituirei le 3 dichiarazioni assertTrue con una singola Hamcrest ...assertThat(values, contains("exception.message", "exception.detail"));
Brad

Il test case di OP non chiama due volte methodToTest ()?
impegnato e

scusa non sono stato chiaro. Mi riferivo allo scenario in cui OP voleva verificare che due argomenti fossero chiamati insieme. Quindi la firma del metodo sarebbe qualcosa di simile a public void methodToTest (Exception e, Message m, ActionErrors errors) {in modo che un'eccezione specifica venga chiamata con un messaggio specifico. Suppongo che potresti avere solo due ArgumentCaptor e quindi recuperare l'indice e confrontare utilizzando i valori in quegli indici in entrambi gli elenchi di valori
committedandroidery

Il caso di test di OP chiama methodToTest()una volta. È l'argomento del metodo che ActionErrors errorsviene chiamato internamente due volte.
Brad il

61

Se l'ordine di entrambe le add()chiamate è pertinente, puoi utilizzare 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
È sufficiente passare un singolo errorsargomento: InOrder inOrder = inOrder(errors);(vedi documenti )
GreenhouseVeg

2
E se l'ordine NON è rilevante? come spesso accade.
haelix

1
@haelix In tal caso, usa la risposta di Brad. Converti Listin Sete asserisci che il set di input è uguale al set dato dall'argomento acquisisce.
flopshot

25

Prova qualcosa di simile:

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

5
Il tuo assegno è ovviamente troppo rilassato.
haelix

17

probabilmente hai un problema nel codice. Perché in realtà scrivi questo codice:

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());

Notare che la prima verifica non è nemmeno in ordine rispetto alle invocazioni effettive.

Inoltre, ti consiglio di non prendere in giro tipi che non possiedi, ad esempio il tipo struts.

[EDIT @Brad]

Dopo aver eseguito il codice di Brice (sopra) nel mio IDE, posso vedere che ho usato ActionError invece di ActionMessage, quindi è per questo che la mia verifica () non corrispondeva. Il messaggio di errore che ho inizialmente pubblicato mi ha indotto a pensare che fosse il primo argomento che non corrispondeva. Si scopre che era il secondo argomento.

Quindi la risposta alla mia domanda è

/** 
 * 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
Non capire quello che stai cercando di dire. L'ordine di verifica è importante? se l'ordine di verifica è importante. Perché allora qui viene fornita l'API InOrder?
Oleksandr Papchenko

Proprio come ciò che è scritto sopra l'ordine di verifica è irrilevante; ecco perché c'è InOrder.
Brice

12

Puoi usare Mockito.atLeastOnce()che consente a Mockito di superare il test anche se quel mockObject verrà chiamato molte volte.

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

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

1

1) Di 'a Mokito l'aspettativa totale delle chiamate.

2) Di 'a Mokito quante volte era prevista ogni combinazione di parametri.

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

In modo simile a @ sendon1928 possiamo usare:

Mockito.times(wantedInvocationCount)

per assicurarsi che il metodo sia stato chiamato il numero esatto di volte (soluzione preferibile a mio parere). Successivamente, possiamo chiamare

Mockito.verifyNoMoreInteractions(mock)

Per assicurarsi che la simulazione non sia stata ulteriormente utilizzata in nessun contesto. Esempio completo:

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

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

Mockito.verifyNoMoreInteractions(mockObject)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.