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

Введение в PowerMock

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

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 .