Краткое руководство по BDDMockito

Краткое руководство по BDDMockito

1. обзор

Термин BDD впервые был придуманDan North – back in 2006.

BDD поощряет написание тестов на естественном, понятном человеку языке, который фокусируется на поведении приложения.

Он определяет четко структурированный способ написания тестов в следующих трех разделах (Arrange, Act, Assert):

  • given некоторые предварительные условия (организовать)

  • when происходит действие (Действие)

  • then проверить вывод (Assert)

The Mockito library is shipped with a BDDMockito class which introduces BDD-friendly APIs. Этот API позволяет нам использовать более дружественный для BDD подход, организуя наши тесты с использованиемgiven() и делая утверждения с использованиемthen().

В этой статье мы расскажем, как настроить тесты Mockito на основе BDD. Мы также поговорим о различиях между APIMockito иBDDMockito, чтобы в конечном итоге сосредоточиться на APIBDDMockito.

2. Настроить

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

The BDD flavor of Mockito is part of the mockito-core library, для начала нам просто нужно включить артефакт:


    org.mockito
    mockito-core
    2.21.0

Для последней версии Mockito проверьтеMaven Central.

2.2. импорт

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

import static org.mockito.BDDMockito.*;

Обратите внимание, чтоBDDMockito расширяетMockito, поэтому мы не пропустим ни одной функции, предоставляемой традиционным APIMockito.

3. Мокито против BDDMockito

Традиционный имитатор в Mockito выполняется с использованиемwhen(obj).then*() на шаге Arrange.

Позже взаимодействие с нашим макетом можно будет проверить с помощьюverify() на шаге Assert.

BDDMockito предоставляет псевдонимы BDD для различных методовMockito, поэтому мы можем написать наш шаг Arrange, используяgiven (вместоwhen), аналогично, мы могли бы написать наш шаг Assert, используяthen (вместоverify).

Давайте посмотрим на пример тестового тела, использующего традиционный 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);

Обратите внимание, как мы поменяли местамиgiven иwill*, это обязательно, если мы имитируем метод, который не имеет возвращаемого значения.

Также обратите внимание, что мы использовали сопоставители аргументов, такие как (any,eq), чтобы обеспечить более общий способ насмешки, основанный на критериях, а не на фиксированном значении.

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

В этом кратком руководстве мы обсудили, как BDDMockito пытается придать сходство BDD с нашими тестами Mockito, и обсудили некоторые различия междуMockito иBDDMockito.

Как всегда, исходный код можно найтиover on GitHub - в тестовом пакетеcom.example.bddmockito.