«1. Обзор

В этом руководстве показано, как использовать ArgumentMatcher и чем он отличается от ArgumentCaptor.

Для ознакомления с фреймворком Mockito обратитесь к этой статье.

2. Зависимости Maven

Нам нужно добавить один артефакт:

<dependency>
    <groupId>org.mockito</groupId> 
    <artifactId>mockito-core</artifactId>
    <version>2.21.0</version> 
    <scope>test</scope>
</dependency>

Последнюю версию Mockito можно найти на Maven Central.

3. ArgumentMatchers

Можно настроить mock-метод различными способами. Один из них — возвращать фиксированные значения:

doReturn("Flower").when(flowerService).analyze("poppy");

В приведенном выше примере строка «Цветок» возвращается только тогда, когда служба анализа получает строку «мак».

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

Во всех этих сценариях мы можем настроить наши фиктивные методы с помощью сопоставителей аргументов:

when(flowerService.analyze(anyString())).thenReturn("Flower");

Теперь, благодаря сопоставителю аргументов anyString, результат будет одинаковым независимо от того, какое значение мы передаем для анализа. ArgumentMatchers позволяет нам гибко проверять или заглушать.

Если метод имеет более одного аргумента, невозможно использовать ArgumentMatchers только для некоторых аргументов. Mockito требует, чтобы вы предоставили все аргументы либо с помощью совпадений, либо с точными значениями.

Следующий пример является неправильным подходом к этому:

abstract class FlowerService {
    public abstract boolean isABigFlower(String name, int petals);
}

FlowerService mock = mock(FlowerService.class);

when(mock.isABigFlower("poppy", anyInt())).thenReturn(true);

Чтобы исправить это и сохранить имя строки «poppy», как мы хотим, мы будем использовать сопоставление eq:

when(mock.isABigFlower(eq("poppy"), anyInt())).thenReturn(true);

Есть еще два момента, на которые следует обратить внимание при использовании сопоставителей:

    Мы не можем использовать их в качестве возвращаемого значения, для заглушки требуется точное значение Наконец, мы не можем использовать сопоставители аргументов вне проверки или заглушки

В последнем случае Mockito обнаружит неуместный аргумент и выдаст исключение InvalidUseOfMatchersException.

Плохим примером может быть:

String orMatcher = or(eq("poppy"), endsWith("y"));
verify(mock).analyze(orMatcher);

Способ реализации приведенного выше кода:

verify(mock).analyze(or(eq("poppy"), endsWith("y")));

Mockito также предоставляет AdditionalMatchers для реализации общих логических операций («не», « и», «или») в сопоставлениях аргументов, которые соответствуют как примитивным, так и непримитивным типам:

verify(mock).analyze(or(eq("poppy"), endsWith("y")));

4. Пользовательское сопоставление аргументов заданному сценарию и произвести тест высочайшего качества, который является чистым и ремонтопригодным.

Например, у нас может быть MessageController, который доставляет сообщения. Он получит MessageDTO и на его основе создаст сообщение, которое будет доставлено MessageService.

Наша проверка будет простой, убедитесь, что мы вызвали MessageService ровно 1 раз с любым сообщением:

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

verify(messageService, times(1)).deliverMessage(any(Message.class));

Этот подход не позволяет нам проверять данные внутри Message, которые могут отличаться от данных внутри MessageDTO.

По этой причине мы собираемся реализовать собственный сопоставитель аргументов:

Чтобы использовать наш сопоставитель, нам нужно изменить наш тест и заменить любой на argThat:

public class MessageMatcher implements ArgumentMatcher<Message> {

    private Message left;

    // constructors

    @Override
    public boolean matches(Message right) {
        return left.getFrom().equals(right.getFrom()) &&
          left.getTo().equals(right.getTo()) &&
          left.getText().equals(right.getText()) &&
          right.getDate() != null &&
          right.getId() != null;
    }
}

Теперь мы знаем наш экземпляр Message будет иметь те же данные, что и наш MessageDTO.

verify(messageService, times(1)).deliverMessage(argThat(new MessageMatcher(message)));

5. Сопоставление настраиваемых аргументов и ArgumentCaptor

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

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

Пользовательские сопоставления аргументов через ArgumentMatcher обычно лучше подходят для заглушек.

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

В этой статье мы рассмотрели функцию Mockito, ArgumentMatcher и ее отличие от ArgumentCaptor.

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

«