«1. Введение

Mockito — популярный фреймворк для имитации Java. С его помощью легко создавать фиктивные объекты, настраивать поведение фиктивных объектов, захватывать аргументы методов и проверять взаимодействие с фиктивными объектами.

Теперь мы сосредоточимся на задании фиктивного поведения. У нас есть два способа сделать это: синтаксис when().thenDoSomething() и doSomething().when().

В этом коротком уроке мы увидим, почему они оба у нас есть.

2. Метод when()

Рассмотрим следующий интерфейс Employee:

interface Employee {
    String greet();
    void work(DayOfWeek day);
}

В наших тестах мы используем макет этого интерфейса. Допустим, мы хотим настроить метод приветствия() мока так, чтобы он возвращал строку «Hello». Это легко сделать с помощью метода Mockito when():

@Test
void givenNonVoidMethod_callingWhen_shouldConfigureBehavior() {
    // given
    when(employee.greet()).thenReturn("Hello");

    // when
    String greeting = employee.greet();

    // then
    assertThat(greeting, is("Hello"));
}

Что происходит? Объект работника является макетом. Когда мы вызываем любой из его методов, Mockito регистрирует этот вызов. С вызовом метода when() Mockito знает, что этот вызов не был взаимодействием бизнес-логики. Это было заявление о том, что мы хотим присвоить некоторое поведение фиктивному объекту. После этого одним из методов thenXxx() мы задаем ожидаемое поведение.

До этого момента это старое доброе издевательство. Точно так же мы хотим настроить метод work() так, чтобы он выдавал исключение, когда мы вызываем его с аргументом Sunday:

@Test
void givenVoidMethod_callingWhen_wontCompile() {
    // given
    when(employee.work(DayOfWeek.SUNDAY)).thenThrow(new IAmOnHolidayException());

    // when
    Executable workCall = () -> employee.work(DayOfWeek.SUNDAY);

    // then
    assertThrows(IAmOnHolidayException.class, workCall);
}

К сожалению, этот код не скомпилируется, потому что в work(employee.work( …)) метод work() имеет возвращаемый тип void; следовательно, мы не можем обернуть его в другой вызов метода. Означает ли это, что мы не можем издеваться над методами void? Конечно, мы можем. Методы doXxx спешат на помощь!

3. Методы doXxx()

Давайте посмотрим, как мы можем настроить генерацию исключений с помощью метода doThrow():

@Test
void givenVoidMethod_callingDoThrow_shouldConfigureBehavior() {
    // given
    doThrow(new IAmOnHolidayException()).when(employee).work(DayOfWeek.SUNDAY);

    // when
    Executable workCall = () -> employee.work(DayOfWeek.SUNDAY);

    // then
    assertThrows(IAmOnHolidayException.class, workCall);
}

Этот синтаксис немного отличается от предыдущего: мы не пытаемся оберните вызов метода void внутри вызова другого метода. Поэтому этот код компилируется.

Давайте посмотрим, что только что произошло. Во-первых, мы заявили, что хотим сгенерировать исключение. Затем мы вызвали метод when() и передали фиктивный объект. После этого мы указали, какое поведение фиктивного взаимодействия мы хотим настроить.

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

Почему у нас есть первая функция when().thenXxx(), если она не способна на такую ​​распространенную задачу, как настройка вызова void? Он имеет множество преимуществ перед синтаксисом doXxx().when().

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

Во-вторых, мы можем добавить несколько вариантов поведения к одному и тому же взаимодействию с помощью цепочки. Это связано с тем, что метод when() возвращает экземпляр класса OngoingStubbing\u003cT\u003e, а методы thenXxx() возвращают тот же тип.

С другой стороны, методы doXxx() возвращают экземпляр Stubber, а Stubber.when(T mock) возвращает T, поэтому мы можем указать, какой вызов метода мы хотим настроить. Но T является частью нашего приложения, например, Employee в наших фрагментах кода. Но T не вернет класс Mockito, поэтому мы не сможем добавить несколько вариантов поведения с помощью цепочки.

4. BDDMockito

BDDMockito использует синтаксис, альтернативный тем, которые мы рассмотрели. Это довольно просто: в наших фиктивных конфигурациях мы должны заменить ключевое слово «когда» на «дано», а ключевое слово «делать» на «будет». В остальном наш код остался прежним:

@Test
void givenNonVoidMethod_callingGiven_shouldConfigureBehavior() {
    // given
    given(employee.greet()).willReturn("Hello");

    // when
    String greeting = employee.greet();

    // then
    assertThat(greeting, is("Hello"));
}

@Test
void givenVoidMethod_callingWillThrow_shouldConfigureBehavior() {
    // given
    willThrow(new IAmOnHolidayException()).given(employee).work(DayOfWeek.SUNDAY);

    // when
    Executable workCall = () -> employee.work(DayOfWeek.SUNDAY);

    // then
    assertThrows(IAmOnHolidayException.class, workCall);
}

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

Мы увидели преимущества и недостатки настройки фиктивного объекта, когда().thenXxx() или doXxx().when( ) способ. Кроме того, мы увидели, как работают эти синтаксисы и почему у нас есть оба.

Как обычно, примеры доступны на GitHub.