2012-11-28 11 views
42

ho qualche codiceCalling callback con Mockito

service.doAction(request, Callback<Response> callback); 

Come posso utilizzare Mockito afferrare l'oggetto callback, e chiamare callback.reply (x)

risposta

55

Si desidera impostare un oggetto che fa Answer quella. Dai un'occhiata alla documentazione Mockito, a https://static.javadoc.io/org.mockito/mockito-core/2.8.47/org/mockito/Mockito.html#answer_stubs

Si potrebbe scrivere qualcosa di simile

when(mockService.doAction(any(Request.class), any(Callback.class))).thenAnswer(
    new Answer<Object>() { 
     Object answer(InvocationOnMock invocation) { 
      ((Callback<Response>) invocation.getArguments()[1]).reply(x); 
      return null; 
     } 
}); 

(sostituendo x con tutto ciò che dovrebbe essere, ovviamente)

+33

Btw, nel caso in cui la funzione restituisca void, è necessario eseguire doAnswer (...). When (mockedObject) .method (any (Callback []. Class)); come spiegato su http://stackoverflow.com/questions/3581754/using-mockito-how-do-i-intercept-a-callback-object-on-a-void-method – Thomas

+0

Ecco un altro link a qualcosa di simile https://testing.googleblog.com/2014/03/whenhow-to-use-mockito-answer.html –

2
when(service.doAction(any(Request.class), any(Callback.class))).thenAnswer(
    new Answer() { 
    Object answer(InvocationOnMock invocation) { 
     Callback<Response> callback = 
        (Callback<Response>) invocation.getArguments()[1]; 
     callback.reply(/*response*/); 
    } 
}); 
35

considerare l'utilizzo di un ArgumentCaptor, che in ogni caso è una corrispondenza ravvicinata a "afferrare [bing] l'oggetto callback".

/** 
* Captor for Response callbacks. Populated by MockitoAnnotations.initMocks(). 
* You can also use ArgumentCaptor.forClass(Callback.class) but you'd have to 
* cast it due to the type parameter. 
*/ 
@Captor ArgumentCaptor<Callback<Response>> callbackCaptor; 

@Test public void testDoAction() { 
    // Cause service.doAction to be called 

    // Now call callback. ArgumentCaptor.capture() works like a matcher. 
    verify(service).doAction(eq(request), callbackCaptor.capture()); 

    assertTrue(/* some assertion about the state before the callback is called */); 

    // Once you're satisfied, trigger the reply on callbackCaptor.getValue(). 
    callbackCaptor.getValue().reply(x); 

    assertTrue(/* some assertion about the state after the callback is called */); 
} 

Mentre un Answer è una buona idea quando il callback deve restituire immediatamente (leggi: sincrono), introduce anche il sovraccarico di creare una classe interna anonima, e non sicuro colata gli elementi invocation.getArguments()[n] al tipo di dati tu vuoi. Richiede inoltre di formulare qualsiasi affermazione sullo stato di pre-callback del sistema da WITHIN the Answer, il che significa che la risposta può aumentare in termini di dimensioni e portata.

Invece, tratta la tua callback in modo asincrono: Cattura l'oggetto Callback passato al tuo servizio usando un ArgumentCaptor. Ora puoi fare tutte le tue asserzioni al livello del metodo di prova e chiamare reply quando lo desideri. Ciò è particolarmente utile se il tuo servizio è responsabile di più callback simultanei, perché hai più controllo sull'ordine in cui i callback restituiscono.

+0

ma puoi farlo se il metodo con il callback viene chiamato indirettamente dal test (in altre parole, viene chiamato da qualcosa che viene attivato dal codice di test ma non nel codice di test)? – Thomas

+0

Generalmente, se stai lavorando con un callback in un test di unità, stai passando un'interfaccia per il tuo sistema in prova a chiamare (che puoi prendere in giro) o stai prendendo in giro il metodo di una dipendenza che _receives_ un callback (quindi devi chiamare tu stesso il callback, come sopra). È atipico che l'interfaccia di callback catturata venga sempre assegnata a un'altra classe da chiamare, ma non c'è motivo per cui non si possa. E 'questo che intendevi? –

+0

È possibile evitare il problema di sicurezza del tipo utilizzando 'invocation.getArgumentAt (1, Callback.class) .reply();' – Steve