Введение в PowerMock
1. обзор
Модульное тестирование с помощью фреймворка mocking уже давно признано полезной практикой, а фреймворкMockito, в частности, доминирует на этом рынке в последние годы.
А для того, чтобы упростить разработку достойного кода и сделать публичный API простым, некоторые желаемые функции были намеренно исключены. Однако в некоторых случаях эти недостатки вынуждают тестировщиков писать громоздкий код, чтобы сделать возможным создание макетов.
Здесь вступает в игру фреймворкPowerMock.
PowerMockito - это API расширения PowerMock для поддержки Mockito. Он предоставляет возможности для работы с Java Reflection API простым способом преодоления проблем Mockito, таких как отсутствие способности имитировать конечные, статические или частные методы.
Из этого туториала вы узнаете, как использовать PowerMockito API и как его применять в тестах.
2. Подготовка к тестированию с PowerMockito
Первым шагом для интеграции поддержки PowerMock для Mockito является включение следующих двух зависимостей в файл POM Maven:
org.powermock
powermock-module-junit4
1.6.4
test
org.powermock
powermock-api-mockito
1.6.4
test
Затем нам нужно подготовить наши тестовые примеры для работы сPowerMockito, применив следующие две аннотации:
@RunWith(PowerMockRunner.class)
@PrepareForTest(fullyQualifiedNames = "com.example.powermockito.introduction.*")
ЭлементfullyQualifiedNames в аннотации@PrepareForTest представляет собой массив полностью определенных имен типов, которые мы хотим имитировать. В этом случае мы используем имя пакета с подстановочным знаком, чтобы указатьPowerMockito подготовить все типы в пакетеcom.example.powermockito.introduction для имитации.
Теперь мы готовы использовать силуPowerMockito.
3. Мокирующие конструкторы и финальные методы
В этом разделе мы продемонстрируем способы получить фиктивный экземпляр вместо реального при создании экземпляра класса с операторомnew, а затем использовать этот объект для имитации финального метода. Сотрудничающий класс, чьи конструкторы и конечные методы будут проверены, определяется следующим образом:
public class CollaboratorWithFinalMethods {
public final String helloMethod() {
return "Hello World!";
}
}
Сначала мы создаем фиктивный объект с помощью APIPowerMockito:
CollaboratorWithFinalMethods mock = mock(CollaboratorWithFinalMethods.class);
Затем установите ожидание, говорящее, что всякий раз, когда вызывается конструктор no-arg этого класса, должен возвращаться фиктивный экземпляр, а не реальный:
whenNew(CollaboratorWithFinalMethods.class).withNoArguments().thenReturn(mock);
Давайте посмотрим, как этот макет конструкции работает в действии, создав экземпляр классаCollaboratorWithFinalMethods с помощью конструктора по умолчанию, а затем проверим поведение PowerMock:
CollaboratorWithFinalMethods collaborator = new CollaboratorWithFinalMethods();
verifyNew(CollaboratorWithFinalMethods.class).withNoArguments();
На следующем шаге ожидание устанавливается для окончательного метода:
when(collaborator.helloMethod()).thenReturn("Hello example!");
Этот метод затем выполняется:
String welcome = collaborator.helloMethod();
Следующие утверждения подтверждают, что методhelloMethod был вызван для объектаcollaborator и возвращает значение, установленное ложным ожиданием:
Mockito.verify(collaborator).helloMethod();
assertEquals("Hello example!", welcome);
Если мы хотим имитировать конкретный финальный метод, а не все финальные внутри объекта, может пригодиться метод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!";
}
}
Чтобы имитировать эти статические методы, нам нужно зарегистрировать включающий класс в APIPowerMockito:
mockStatic(CollaboratorWithStaticMethods.class);
В качестве альтернативы мы можем использовать методMockito.spy(Class<T> class), чтобы имитировать конкретный, как показано в следующем разделе.
Затем можно установить ожидания для определения значений, которые должны возвращать методы при вызове:
when(CollaboratorWithStaticMethods.firstMethod(Mockito.anyString()))
.thenReturn("Hello example!");
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 example!", firstWelcome);
assertEquals("Hello example!", secondWelcome);
Мы также можем проверить поведение имитационных методов, включая то, сколько раз метод вызывается. В этом случаеfirstMethod вызывается дважды, аsecondMethod никогда:
verifyStatic(Mockito.times(2));
CollaboratorWithStaticMethods.firstMethod(Mockito.anyString());
verifyStatic(Mockito.never());
CollaboratorWithStaticMethods.secondMethod();
Note: МетодverifyStatic должен вызываться непосредственно перед любой проверкой статического метода дляPowerMockito, чтобы знать, что последующий вызов метода - это то, что необходимо проверить.
Наконец, статический методthirdMethod должен выдаватьRuntimeException, как было объявлено ранее в макете. Это подтверждается элементомexpected аннотации@Test:
@Test(expected = RuntimeException.class)
public void givenStaticMethods_whenUsingPowerMockito_thenCorrect() {
// other methods
CollaboratorWithStaticMethods.thirdMethod();
}
5. Частичное издевательство
Вместо издевательства над всем классом APIPowerMockito позволяет имитировать его часть с помощью методаspy. Следующий класс будет использоваться в качестве коллаборатора для иллюстрации поддержки PowerMock для частичной имитации:
public class CollaboratorForPartialMocking {
public static String staticMethod() {
return "Hello example!";
}
public final String finalMethod() {
return "Hello example!";
}
private String privateMethod() {
return "Hello example!";
}
public String privateMethodCaller() {
return privateMethod() + " Welcome to the Java world.";
}
}
Начнем с имитации статического метода, который названstaticMethod в приведенном выше определении класса. Во-первых, используйте APIPowerMockito, чтобы частично смоделировать класс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);
Теперь пришло время перейти к финальным и приватным методам. Чтобы проиллюстрировать частичное высмеивание этих методов, нам нужно создать экземпляр класса и сообщить APIPowerMockitospy:
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. Заключение
Это руководство представляет собой введение в APIPowerMockito, демонстрируя его использование для решения некоторых проблем, с которыми сталкиваются разработчики при использовании инфраструктуры Mockito.
Реализацию этих примеров и фрагментов кода можно найти вthe linked GitHub project.