Тестирование обратных вызовов с Mockito

Тестирование обратных вызовов с Mockito

1. обзор

В этом коротком руководстве мы сосредоточимся наhow to test Callbacks с использованием популярной среды тестированияMockito.

Мы рассмотрим два решения,firstly using an ArgumentCaptor and then the intuitive doAnswer() method.

Чтобы узнать больше о хорошем тестировании с помощью Mockito, ознакомьтесь с нашей серией Mockitohere.

2. Введение в обратные вызовы

A callback is a piece of code that is passed as an argument to a method, which is expected to call back (execute) the argument at a given time.

Это выполнение может быть немедленным, как при синхронном обратном вызове, но, как правило, это может произойти позже, как при асинхронном обратном вызове.

Обычный сценарий использования обратных вызовов -during service interactions when we need to process the response from a service call.

В этом руководстве мы будем использовать интерфейсService, показанный ниже, в качестве соавтора в тестовых примерах:

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

В аргументеCallback мы передаем класс, который будет обрабатывать ответ с помощью методаreply(T response):

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

2.1. Простое обслуживание

Мы также будем использовать простойservice example to demonstrate how to pass and invoke the callback:

public void doAction() {
    service.doAction("our-request", new Callback() {
        @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 Lamda, аservice.doActioncall could also be written more concisely:

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

Чтобы узнать больше о лямбда-выражениях, посмотритеhere.

3. ИспользуяArgumentCaptor

Теперь посмотрим наhow we use Mockito to grab the Callback object using an*ArgumentCaptor*:

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

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

    Callback 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 этого обработчика. This is simply a wrapper to our Simple Service doAction method call, где мы вызываем наш обратный вызов.

Затем мы проверяем, чтоdoAction was вызвал наш экземпляр фиктивного сервиса, передавanyString() в качестве первого аргумента иcallbackCaptor.capture() as the second, which is where we capture the Callback object. Затем можно использовать методgetValue() для возврата захваченного значения аргумента.

Теперь, когда у нас есть объектCallback, мы создаем объектResponse, который действителен по умолчанию до того, как мыcall the reply method directly and assert that the response data has the correct value.

4. Использование методаdoAnswer()

Теперь посмотрим наcommon solution for stubbing methods that have callbacks, используя MockitoAnswer object and doAnswer method to stub the void methoddoAction:.

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

    doAnswer((Answer) invocation -> {
        Callback 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, который будет использоваться позже в тесте.

Затем мы настраиваемAnswer в нашем фиктивном сервисе так, чтобы при вызовеdoActionwe intercept the invocation and grab the method arguments using invocation.getArgument(1) получал аргументCallback

Последний шаг - создать вызовActionHandler anddoAction, который вызывает вызовAnswer.

Чтобы узнать больше о методах заглушки void, посмотритеhere.

3. Заключение

В этой краткой статье мы рассмотрели два различных подхода к тестированию обратных вызовов при тестировании с помощью Mockito.

Как всегда, примеры доступны в этомGitHub project.