«1. Обзор

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

Мы рассмотрим два решения, сначала используя ArgumentCaptor, а затем интуитивно понятный метод doAnswer().

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

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

Обратный вызов — это фрагмент кода, который передается в качестве аргумента методу, который, как ожидается, вызовет (выполнит) аргумент в заданное время.

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

Обычный сценарий использования обратных вызовов — во время взаимодействия службы, когда нам нужно обработать ответ от вызова службы.

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

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

В аргументе обратного вызова мы передаем класс, который будет обрабатывать ответ, используя ответ (T ответ) метод:

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 Lamda, но вызов service.doAction также можно было бы написать более лаконично:

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

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

3. Использование ArgumentCaptor

Теперь давайте посмотрим, как мы используем Mockito для захвата объекта обратного вызова с помощью ArgumentCaptor:

@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 Simple Service, в котором мы вызываем наш обратный вызов.

Затем мы проверяем, что doAction был вызван для нашего фиктивного экземпляра службы, передавая anyString() в качестве первого аргумента и callbackCaptor.capture() в качестве второго, и именно здесь мы захватываем объект обратного вызова. Затем можно использовать метод getValue() для возврата захваченного значения аргумента.

Теперь, когда у нас есть объект обратного вызова, мы создаем объект Response, который является допустимым по умолчанию, прежде чем мы вызовем метод ответа напрямую и утверждаем, что данные ответа имеют правильное значение.

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

Теперь мы рассмотрим распространенное решение для заглушек методов, которые имеют обратные вызовы, используя объект Mockito Answer и метод doAnswer для заглушки метода void doAction:

@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, который будет использоваться позже в тесте.

Затем мы настраиваем ответ в нашей фиктивной службе, чтобы при вызове doAction мы перехватывали вызов и получали аргументы метода, используя invocation.getArgument(1), чтобы получить аргумент обратного вызова.

Последним шагом является создание ActionHandler и вызов doAction, который вызывает вызов ответа.

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

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

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

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