使用Mockito测试回调

2023/05/12

1. 概述

在这个简短的教程中,我们将重点介绍如何使用Mockito测试回调。

我们介绍两种解决方案,首先使用ArgumentCaptor,然后使用更直观的doAnswer()方法。

2. 回调简介

回调是作为参数传递给方法的一段代码,该方法应在给定时间回调(执行)参数

此执行可能会像在同步回调中一样立即执行,但更常见的是,它可能会在稍后的时间发生,就像在异步回调中一样。

使用回调的一个常见场景是在服务交互期间,当我们需要处理来自服务调用的响应时。

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

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

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

public interface Callback<T> {

    void reply(T response);
}

2.1 Service

我们还将使用一个简单的Service来演示如何传递和调用回调

public class ActionHandler {

    private final Service service;

    public ActionHandler(Service service) {
        this.service = service;
    }

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

在将一些数据添加到response对象之前,handleResponses方法检查响应是否有效:

public class ActionHandler {

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

为了清楚起见,我们在service.doAction()调用中没有使用Lambda表达式,如果使用Lambda代码如下:

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

3. 使用ArgumentCaptor

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

class ActionHandlerUnitTest {

    @Mock
    private Service service;

    @Captor
    private ArgumentCaptor<Callback<Response>> callbackCaptor;

    @BeforeEach
    void setup() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    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(expectedMessage, data.getMessage(), "Should receive a successful message: ");
    }
}

在这个例子中,我们首先创建一个ActionHandler,然后再调用这个handler的doAction方法。 这只是Service中doAction方法调用的包装器,我们在其中调用回调。

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

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

4. 使用doAnswer()方法

现在我们来看一个常见的stubbing方法的解决方案,这种方式使用Mockito的Answer对象和doAnswer方法来stub方法doAction:

class ActionHandlerUnitTest {

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

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

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

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

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

接下来,我们在mock Service上设置Answer,以便在调用doAction时拦截调用并使用invocation.getArgument(1)获取方法参数以获取Callback参数。

最后一步是创建ActionHandler并调用doAction,从而调用Answer。

5. 总结

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

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章