1. Обзор
Термин BDD впервые был придуман Дэном Нортом — еще в 2006 году .
BDD поощряет написание тестов на естественном, понятном человеку языке, который фокусируется на поведении приложения.
Он определяет четко структурированный способ написания тестов, следующих трем разделам (Arrange, Act, Assert):
при
некоторых предварительных условиях (аранжировка)когда
происходит действие (Act)затем
проверьте вывод (Утвердить)
Библиотека Mockito поставляется с классом BDDMockito
, который представляет API-интерфейсы, удобные для BDD. Этот API позволяет нам использовать более дружественный к BDD подход, организуя наши тесты с помощью Given()
и делая утверждения с помощью then()
.
В этой статье мы собираемся объяснить, как настроить наши тесты Mockito на основе BDD. Мы также поговорим о различиях между API Mockito
и BDDMockito
, чтобы в конечном итоге сосредоточиться на API BDDMockito
.
2. Настройка
2.1. Зависимости Maven
Вариант BDD Mockito является частью библиотеки mockito-core
, чтобы начать работу, нам просто нужно включить артефакт:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.21.0</version>
</dependency>
Для получения последней версии Mockito, пожалуйста, проверьте Maven Central .
2.2. Импорт
Наши тесты могут стать более читабельными, если мы включим следующий статический импорт:
import static org.mockito.BDDMockito.*;
Обратите внимание, что BDDMockito
расширяет Mockito
, поэтому мы не упустим ни одной функции, предоставляемой традиционным API Mockito
.
3. Мокито против BDDMockito
Традиционное издевательство в Mockito выполняется с помощью when(obj)
. then*()
на шаге аранжировки.
Позже взаимодействие с нашим макетом можно проверить с помощью функции verify()
на этапе Assert.
BDDMockito
предоставляет псевдонимы BDD для различных методов Mockito
, поэтому мы можем написать наш шаг аранжировки, используя заданный
(вместо когда
), точно так же мы можем написать наш шаг утверждения, используя тогда
(вместо проверки
).
Давайте рассмотрим пример тестового тела с использованием традиционного Mockito:
when(phoneBookRepository.contains(momContactName))
.thenReturn(false);
phoneBookService.register(momContactName, momPhoneNumber);
verify(phoneBookRepository)
.insert(momContactName, momPhoneNumber);
Давайте посмотрим, как это сравнивается с BDDMockito
:
given(phoneBookRepository.contains(momContactName))
.willReturn(false);
phoneBookService.register(momContactName, momPhoneNumber);
then(phoneBookRepository)
.should()
.insert(momContactName, momPhoneNumber);
4. Издевательство над BDDMockito
Давайте попробуем протестировать PhoneBookService
, где нам нужно смоделировать PhoneBookRepository:
public class PhoneBookService {
private PhoneBookRepository phoneBookRepository;
public void register(String name, String phone) {
if(!name.isEmpty() && !phone.isEmpty()
&& !phoneBookRepository.contains(name)) {
phoneBookRepository.insert(name, phone);
}
}
public String search(String name) {
if(!name.isEmpty() && phoneBookRepository.contains(name)) {
return phoneBookRepository.getPhoneNumberByContactName(name);
}
return null;
}
}
BDDMockito
как Mockito
позволяет нам возвращать значение, которое может быть фиксированным или динамическим. Это также позволило бы нам генерировать исключение:
4.1. Возврат фиксированного значения
Используя BDDMockito,
мы могли бы легко настроить Mockito для возврата фиксированного результата всякий раз, когда вызывается наш целевой метод фиктивного объекта:
given(phoneBookRepository.contains(momContactName))
.willReturn(false);
phoneBookService.register(xContactName, "");
then(phoneBookRepository)
.should(never())
.insert(momContactName, momPhoneNumber);
4.2. Возврат динамического значения
BDDMockito
позволяет нам предоставить более сложный способ возврата значений. Мы могли бы вернуть динамический результат на основе ввода:
given(phoneBookRepository.contains(momContactName))
.willReturn(true);
given(phoneBookRepository.getPhoneNumberByContactName(momContactName))
.will((InvocationOnMock invocation) ->
invocation.getArgument(0).equals(momContactName)
? momPhoneNumber
: null);
phoneBookService.search(momContactName);
then(phoneBookRepository)
.should()
.getPhoneNumberByContactName(momContactName);
4.3. Создание исключения
Сказать Mockito генерировать исключение довольно просто:
given(phoneBookRepository.contains(xContactName))
.willReturn(false);
willThrow(new RuntimeException())
.given(phoneBookRepository)
.insert(any(String.class), eq(tooLongPhoneNumber));
try {
phoneBookService.register(xContactName, tooLongPhoneNumber);
fail("Should throw exception");
} catch (RuntimeException ex) { }
then(phoneBookRepository)
.should(never())
.insert(momContactName, tooLongPhoneNumber);
Обратите внимание, как мы поменяли местами данные
и will*
, это обязательно, если мы имитируем метод, который не имеет возвращаемого значения.
Также обратите внимание, что мы использовали сопоставители аргументов, такие как ( any
, eq
), чтобы обеспечить более общий способ имитации на основе критериев, а не в зависимости от фиксированного значения.
5. Вывод
В этом кратком руководстве мы обсудили, как BDDMockito пытается добиться сходства BDD с нашими тестами Mockito, а также обсудили некоторые различия между Mockito
и BDDMockito
.
Как всегда, исходный код можно найти на GitHub — в тестовом пакете com.foreach.bddmockito
.