Перейти к основному содержимому

Разница между методами when() и doXxx() в Mockito

· 4 мин. чтения

1. Введение

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

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

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

2. метод когда()

Рассмотрим следующий интерфейс 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<T> , а методы thenXxx() возвращают тот же тип.

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

4. БДМокито

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. Вывод

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

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