1. Обзор
Модульное тестирование с помощью мок-фреймворка уже давно признано полезной практикой, и в последние годы на этом рынке доминирует фреймворк Mockito .
И чтобы упростить дизайн кода и сделать общедоступный API простым, некоторые желаемые функции были намеренно опущены. Однако в некоторых случаях эти недостатки вынуждают тестировщиков писать громоздкий код только для того, чтобы сделать возможным создание макетов.
Здесь в игру вступает фреймворк PowerMock .
PowerMockito
— это API расширения PowerMock для поддержки Mockito. Он предоставляет возможности для работы с Java Reflection API простым способом для преодоления проблем Mockito, таких как отсутствие возможности имитировать окончательные, статические или частные методы.
В этом руководстве будет представлен API PowerMockito и рассмотрено его применение в тестах.
2. Подготовка к тестированию с PowerMockito
Первым шагом для интеграции поддержки PowerMock для Mockito является включение следующих двух зависимостей в файл Maven POM:
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.6.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.6.4</version>
<scope>test</scope>
</dependency>
Далее нам нужно подготовить наши тестовые примеры для работы с PowerMockito
, применив следующие две аннотации:
@RunWith(PowerMockRunner.class)
@PrepareForTest(fullyQualifiedNames = "com.foreach.powermockito.introduction.*")
Элемент fullQualifiedNames
в аннотации @PrepareForTest
представляет собой массив полных имен типов, которые мы хотим имитировать. В этом случае мы используем имя пакета с подстановочным знаком, чтобы указать PowerMockito
подготовить все типы в пакете com.foreach.powermockito.introduction
для имитации.
Теперь мы готовы использовать мощь PowerMockito
.
3. Мокающие конструкторы и окончательные методы
В этом разделе мы покажем, как получить фиктивный экземпляр вместо реального при создании экземпляра класса с помощью оператора new
, а затем использовать этот объект для имитации финального метода.
Вот как мы определим сотрудничающий класс, чьи конструкторы и окончательные методы будут имитированы:
public class CollaboratorWithFinalMethods {
public final String helloMethod() {
return "Hello World!";
}
}
Во-первых, мы создаем фиктивный объект, используя API PowerMockito
:
CollaboratorWithFinalMethods mock = mock(CollaboratorWithFinalMethods.class);
Затем мы устанавливаем ожидание, говорящее, что всякий раз, когда вызывается конструктор без аргументов этого класса, должен быть возвращен фиктивный экземпляр, а не реальный:
whenNew(CollaboratorWithFinalMethods.class).withNoArguments().thenReturn(mock);
Давайте посмотрим, как эта имитация конструкции работает в действии, создав экземпляр класса CollaboratorWithFinalMethods
с помощью его конструктора по умолчанию, а затем мы проверим поведение PowerMock:
CollaboratorWithFinalMethods collaborator = new CollaboratorWithFinalMethods();
verifyNew(CollaboratorWithFinalMethods.class).withNoArguments();
На следующем шаге ожидание задается для конечного метода:
when(collaborator.helloMethod()).thenReturn("Hello ForEach!");
Затем выполняется этот метод:
String welcome = collaborator.helloMethod();
Следующие утверждения подтверждают, что метод helloMethod
был вызван для объекта- сотрудника
и возвращает значение, установленное имитирующим ожиданием:
Mockito.verify(collaborator).helloMethod();
assertEquals("Hello ForEach!", welcome);
Если мы хотим смоделировать конкретный метод final, а не все методы final внутри объекта, может пригодиться метод Mockito.spy(T object) .
Это показано в разделе 5.
4. Имитация статических методов
Предположим, мы хотим имитировать статические методы класса CollaboratorWithStaticMethods
.
Вот как мы объявим этот класс:
public class CollaboratorWithStaticMethods {
public static String firstMethod(String name) {
return "Hello " + name + " !";
}
public static String secondMethod() {
return "Hello no one!";
}
public static String thirdMethod() {
return "Hello no one again!";
}
}
Чтобы смоделировать эти статические методы, нам нужно зарегистрировать включающий класс с помощью API PowerMockito
:
mockStatic(CollaboratorWithStaticMethods.class);
В качестве альтернативы мы можем использовать метод Mockito.spy(Class<T> class)
для имитации определенного, как показано в следующем разделе.
Затем можно установить ожидания, чтобы определить значения, которые методы должны возвращать при вызове:
when(CollaboratorWithStaticMethods.firstMethod(Mockito.anyString()))
.thenReturn("Hello ForEach!");
when(CollaboratorWithStaticMethods.secondMethod()).thenReturn("Nothing special");
Или может быть задано исключение при вызове метода ThirdMethod
:
doThrow(new RuntimeException()).when(CollaboratorWithStaticMethods.class);
CollaboratorWithStaticMethods.thirdMethod();
Теперь пришло время запустить первые два метода:
String firstWelcome = CollaboratorWithStaticMethods.firstMethod("Whoever");
String secondWelcome = CollaboratorWithStaticMethods.firstMethod("Whatever");
Вместо вызова членов реального класса вышеупомянутые вызовы делегируются методам макета.
Эти утверждения доказывают, что мокап сработал:
assertEquals("Hello ForEach!", firstWelcome);
assertEquals("Hello ForEach!", secondWelcome);
Мы также можем проверить поведение методов макета, в том числе количество вызовов метода.
В этом случае первый метод
вызывался дважды, а второй метод
никогда не вызывался:
verifyStatic(Mockito.times(2));
CollaboratorWithStaticMethods.firstMethod(Mockito.anyString());
verifyStatic(Mockito.never());
CollaboratorWithStaticMethods.secondMethod();
Примечание. Метод verifyStatic
должен вызываться непосредственно перед любой проверкой статического метода, чтобы PowerMockito
знал, что последовательный вызов метода — это то, что необходимо проверить.
Наконец, статический метод ThirdMethod
должен генерировать исключение RuntimeException
, как было объявлено в макете ранее.
Это проверяется ожидаемым
элементом аннотации @Test
:
@Test(expected = RuntimeException.class)
public void givenStaticMethods_whenUsingPowerMockito_thenCorrect() {
// other methods
CollaboratorWithStaticMethods.thirdMethod();
}
5. Частичная насмешка
Вместо того, чтобы имитировать весь класс, PowerMockito
API позволяет имитировать его часть, используя шпионский
метод.
Этот класс будет использоваться в качестве соавтора, чтобы проиллюстрировать поддержку PowerMock для частичного имитации:
public class CollaboratorForPartialMocking {
public static String staticMethod() {
return "Hello ForEach!";
}
public final String finalMethod() {
return "Hello ForEach!";
}
private String privateMethod() {
return "Hello ForEach!";
}
public String privateMethodCaller() {
return privateMethod() + " Welcome to the Java world.";
}
}
Давайте начнем с имитации статического метода, который назван staticMethod
в приведенном выше определении класса.
Во-первых, мы используем API PowerMockito
, чтобы частично имитировать класс CollaboratorForPartialMocking
и установить ожидание для его статического метода:
spy(CollaboratorForPartialMocking.class);
when(CollaboratorForPartialMocking.staticMethod()).thenReturn("I am a static mock method.");
Затем выполняется статический метод:
returnValue = CollaboratorForPartialMocking.staticMethod();
Насмешливое поведение подтверждено:
verifyStatic();
CollaboratorForPartialMocking.staticMethod();
Следующее утверждение подтверждает, что фиктивный метод действительно был вызван, сравнивая возвращаемое значение с ожидаемым:
assertEquals("I am a static mock method.", returnValue);
Теперь пора перейти к финальным и приватным методам.
Чтобы проиллюстрировать частичное издевательство над этими методами, нам нужно создать экземпляр класса и сообщить PowerMockito
API, чтобы он следил за
ним:
CollaboratorForPartialMocking collaborator = new CollaboratorForPartialMocking();
CollaboratorForPartialMocking mock = spy(collaborator);
Созданные выше объекты используются для демонстрации имитации как финальных, так и приватных методов.
Теперь мы займемся последним методом, установив ожидание и вызвав метод:
when(mock.finalMethod()).thenReturn("I am a final mock method.");
returnValue = mock.finalMethod();
Поведение частичного издевательства над этим методом доказано:
Mockito.verify(mock).finalMethod();
Тест проверяет, что вызов метода finalMethod
вернет значение, соответствующее ожиданиям:
assertEquals("I am a final mock method.", returnValue);
Аналогичный процесс применяется к частному методу. Основное отличие состоит в том, что мы не можем напрямую вызвать этот метод из тестового примера.
По сути, приватный метод вызывается другими из того же класса. В классе CollaboratorForPartialMocking метод
privateMethod
вызывается методом privateMethodCaller
, и последний мы будем использовать в качестве делегата.
Начнем с ожидания и вызова:
when(mock, "privateMethod").thenReturn("I am a private mock method.");
returnValue = mock.privateMethodCaller();
Насмешка над приватным методом подтверждается:
verifyPrivate(mock).invoke("privateMethod");
Следующий тест гарантирует, что возвращаемое значение при вызове закрытого метода совпадает с ожидаемым:
assertEquals("I am a private mock method. Welcome to the Java world.", returnValue);
6. Заключение
В этой статье представлен API PowerMockito
, демонстрирующий его использование для решения некоторых проблем, с которыми сталкиваются разработчики при использовании платформы Mockito.
Реализацию этих примеров и фрагментов кода можно найти в связанном проекте GitHub .