Java 用Mockito调用回调

Java 用Mockito调用回调,java,testing,mockito,Java,Testing,Mockito,我有一些密码 service.doAction(request, Callback<Response> callback); service.doAction(请求、回调); 如何使用Mockito获取callback对象并调用callback.reply(x)您想设置一个执行该操作的Answer对象。请参阅Mockito文档,位于 你可能会写这样的东西 when(mockService.doAction(any(Request.class), any(Callback.cla

我有一些密码

service.doAction(request, Callback<Response> callback);
service.doAction(请求、回调);

如何使用Mockito获取callback对象并调用callback.reply(x)

您想设置一个执行该操作的
Answer
对象。请参阅Mockito文档,位于

你可能会写这样的东西

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;
        }
});
when(mockService.doAction(any(Request.class)、any(Callback.class))。然后回答(
新答案(){
对象应答(调用锁调用){
((回调)invocation.getArguments()[1]).reply(x);
返回null;
}
});
(当然,将
x
替换为它应该是的内容)

当(service.doAction(any(Request.class)、any(Callback.class)),然后回答(
新答案(){
对象应答(调用锁调用){
回调=
(回调)invocation.getArguments()[1];
回复(/*response*/);
}
});
考虑使用,在任何情况下,它都更接近于“抓取回调对象”

/**
*响应回调的捕获者。由MockitoAnnotations.initMocks()填充。
*您也可以使用ArgumentCaptor.forClass(Callback.class),但必须
*根据类型参数强制转换它。
*/
@Captor ArgumentCaptor callbackCaptor;
@测试公共void testDoAction(){
//导致调用service.doAction
//现在调用callback.ArgumentCaptor.capture()就像一个匹配器一样工作。
验证(service).doAction(eq(request),callbackCaptor.capture());
assertTrue(/*关于调用回调之前的状态的一些断言*/);
//满足后,在callbackCaptor.getValue()上触发应答。
callbackCaptor.getValue().reply(x);
assertTrue(/*调用回调后有关状态的一些断言*/);
}
当回调需要立即返回(读取:同步)时,
应答是一个好主意,但它也引入了创建匿名内部类的开销,并将
调用.getArguments()[n]
中的元素安全地强制转换为所需的数据类型。它还要求您在应答中对系统的预回调状态做出任何断言,这意味着您的应答的大小和范围可能会增加


相反,异步处理回调:使用ArgumentCaptor捕获传递给服务的回调对象。现在,您可以在测试方法级别进行所有断言,并在选择时调用
reply
。如果您的服务负责多个同时回调,这一点尤其有用,因为您可以更好地控制回调返回的顺序。

如果您有以下方法:-

public void registerListener(final IListener listener) {
    container.registerListener(new IListener() {
        @Override
        public void beforeCompletion() {
        }

        @Override
        public void afterCompletion(boolean succeeded) {
            listener.afterCompletion(succeeded);
        }
    });
}
然后,您可以通过以下方式轻松模拟上述方法:-

@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);
}
@Mock private-IListener侦听器;
@试验
公共无效测试(u registerListener){
target.registerListener(侦听器);
议论文监听器监听器监听器=
ArgumentCaptor.forClass(IListener.class);
验证(container.registerListener(listenerCaptor.capture());
listenerCaptor.getValue().afterCompletion(true);
验证(侦听器)。完成后(true);
}

我希望这可能会对某些人有所帮助,因为我花了很多时间来研究这个解决方案

,但是如果测试间接调用了带有回调的方法(换句话说,它是由测试代码触发但不在测试代码中的某个对象调用的),您能做到这一点吗,如果您在单元测试中使用回调,那么您要么传递被测系统要调用的接口(您可以模拟该接口),要么模拟接收回调的依赖项方法(因此您必须自己调用回调,如上所述)。将捕获的回调接口交给另一个类调用是很不寻常的,但是没有理由不这样做。这就是您的意思吗?您可以通过使用
invocation.getArgumentAt(1,Callback.class).reply()来避免类型安全问题@Steve,这并没有避免这个问题,只是在方法调用中隐藏了强制转换。您仍然在运行时断言
参数[1]
是一个回调,而不是一个对象,这是编译器无法保证的。没什么大不了的,但这是一个区别。顺便说一句,如果函数返回void,则需要执行doAnswer(…).when(mockedObject).method(any(Callback[].class));正如在这里解释的,还有一个类似的链接,我在网络检查中得到了nullPointerException。你能分享一些关于android中api测试的文章或教程吗?你的问题似乎超出了这个问题的范围,所以请提出一个不同的问题
public void registerListener(final IListener listener) {
    container.registerListener(new IListener() {
        @Override
        public void beforeCompletion() {
        }

        @Override
        public void afterCompletion(boolean succeeded) {
            listener.afterCompletion(succeeded);
        }
    });
}
@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);
}