1. 概述

在这个简短的教程中,我们将专注于如何使用流行的测试框架Mockito来测试回调函数

我们将探讨两种解决方案:首先使用ArgumentCaptor,然后是直观的doAnswer()方法

要了解更多关于使用Mockito进行良好测试的信息,请查看我们的Mockito系列教程:这里

2. 回调函数简介

回调函数是一种作为参数传递给方法的代码,预期在给定时间执行这个参数。这种执行可能是同步的,如立即执行,但更常见的是异步的。

回调函数的一个常见应用场景是在服务交互中处理服务调用的响应。

在本教程中,我们将使用下面所示的Service接口作为测试用例中的协作方:

public interface Service {
    void doAction(String request, Callback<Response> callback);
}

在回调参数中,我们传递一个类,它将使用reply(T response)方法处理响应:

public interface Callback<T> {
    void reply(T response);
}

2.1. 简单的服务

我们将使用一个简单的服务示例来演示如何传递和调用回调:

public void doAction() {
    service.doAction("our-request", new Callback<Response>() {
        @Override
        public void reply(Response response) {
            handleResponse(response);
        }
    });
}

handleResponse方法会在添加一些数据到Response对象之前检查响应是否有效:

private void handleResponse(Response response) {
    if (response.isValid()) {
        response.setData(new Data("Successful data response"));
    }
}

为了清晰起见,我们没有使用Java Lambda表达式,但service.doAction调用也可以更简洁地写成:

service.doAction("our-request", response -> handleResponse(response));

有关Lambda表达式的更多信息,请参阅:这里

3. 使用ArgumentCaptor

现在让我们看看如何使用Mockito通过ArgumentCaptor来获取Callback对象:

@Test
public void givenServiceWithValidResponse_whenCallbackReceived_thenProcessed() {
    ActionHandler handler = new ActionHandler(service);
    handler.doAction();

    verify(service).doAction(anyString(), callbackCaptor.capture());

    Callback<Response> callback = callbackCaptor.getValue();
    Response response = new Response();
    callback.reply(response);

    String expectedMessage = "Successful data response";
    Data data = response.getData();
    assertEquals(
      "Should receive a successful message: ", 
      expectedMessage, data.getMessage());
}

在这个例子中,我们首先创建一个ActionHandler,然后调用该处理器的doAction方法。这仅仅是简单地包装了我们简单服务的doAction方法调用,这就是我们调用回调的地方。

接下来,我们验证doAction方法确实被调用了,并且第一个参数为anyString(),第二个参数为callbackCaptor.capture(),这是捕获Callback对象的地方。然后可以使用getValue()方法返回捕获的参数值。

现在我们有了Callback对象,我们可以创建一个默认有效的Response对象,然后直接调用reply方法并断言响应数据具有正确的值。

4. 使用doAnswer()方法

现在,我们将看看如何使用Mockito的Answer对象和doAnswer方法来处理带有回调的虚方法:

@Test
public void givenServiceWithInvalidResponse_whenCallbackReceived_thenNotProcessed() {
    Response response = new Response();
    response.setIsValid(false);

    doAnswer((Answer<Void>) invocation -> {
        Callback<Response> callback = invocation.getArgument(1);
        callback.reply(response);

        Data data = response.getData();
        assertNull("No data in invalid response: ", data);
        return null;
    }).when(service)
        .doAction(anyString(), any(Callback.class));

    ActionHandler handler = new ActionHandler(service);
    handler.doAction();
}

在第二个示例中,我们首先创建一个无效的Response对象,稍后将在测试中使用。

接下来,我们在mock服务上设置Answer,当doAction被调用时,我们拦截调用并使用invocation.getArgument(1)获取Callback参数。

最后一步是创建ActionHandler并调用doAction,这将触发Answer的执行。

有关处理虚方法的更多信息,请参阅:这里

3. 总结

在这篇简短的文章中,我们介绍了使用Mockito测试回调函数的两种不同方法。

如往常一样,示例代码可以在这个GitHub项目中找到。