J'ai du code
service.doAction(request, Callback<Response> callback);
Comment puis-je utiliser Mockito pour récupérer l'objet de rappel et appeler callback.reply (x)
Vous voulez configurer un objet Answer
qui fait cela. Jetez un œil à la documentation Mockito, sur https://static.javadoc.io/org.mockito/mockito-core/2.8.47/org/mockito/Mockito.html#answer_stubs
Vous pourriez écrire quelque chose comme
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;
}
});
(en remplaçant x
par ce qu'il devrait être, bien sûr)
Pensez à utiliser un ArgumentCaptor , qui dans tous les cas est une correspondance plus étroite pour "saisir [bing] l'objet de rappel".
/**
* 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 */);
}
Bien qu'un Answer
soit une bonne idée lorsque le rappel doit retourner immédiatement (lire: de manière synchrone), il introduit également la surcharge de la création d'une classe interne anonyme et de la conversion non sécurisée des éléments à partir de invocation.getArguments()[n]
au type de données souhaité. Cela vous oblige également à faire des affirmations sur l'état de pré-rappel du système à partir de WITHIN the Answer, ce qui signifie que votre réponse peut augmenter en taille et en portée.
Au lieu de cela, traitez votre rappel de manière asynchrone: capturez l'objet de rappel transmis à votre service à l'aide d'un ArgumentCaptor. Vous pouvez maintenant faire toutes vos assertions au niveau de la méthode de test et appeler reply
quand vous le souhaitez. Cela est particulièrement utile si votre service est responsable de plusieurs rappels simultanés, car vous avez plus de contrôle sur l'ordre de retour des rappels.
Si vous avez une méthode comme: -
public void registerListener(final IListener listener) {
container.registerListener(new IListener() {
@Override
public void beforeCompletion() {
}
@Override
public void afterCompletion(boolean succeeded) {
listener.afterCompletion(succeeded);
}
});
}
Ensuite, de la manière suivante, vous pouvez facilement vous moquer de la méthode ci-dessus: -
@Mock private IListener listener;
@Test
public void test_registerListener() {
target.registerListener(listener);
ArgumentCaptor<IListener> listenerCaptor =
ArgumentCaptor.forClass(IListener.class);
verify(container).registerListener(listenerCaptor.capture());
listenerCaptor.getValue().afterCompletion(true);
verify(listener).afterCompletion(true);
}
J'espère que cela pourrait aider quelqu'un, car j'avais passé beaucoup de temps à trouver cette solution
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*/);
}
});