Использование PowerMock с JUnit и Mockito

PowerMock — это библиотека для имитации с открытым исходным кодом для приложений Java. Она расширяет существующие фреймворки имитации, такие как EasyMock и Mockito, добавляя к ним еще более мощные функции. PowerMock позволяет нам писать хорошие модульные тесты даже для самого нетестируемого кода. Например, большинство фреймворков имитации в Java не могут имитировать статические методы или финальные классы. Но с помощью PowerMock мы можем имитировать практически любой класс.

PowerMock в настоящее время расширяет фреймворки EasyMock и Mockito mocking. В зависимости от того, какое расширение предпочтительнее, синтаксис для написания любого модульного теста немного отличается. В этом руководстве мы используем PowerMock с Mockito.

В этом руководстве по PowerMock будет продемонстрирован очень простой пример создания имитатора с использованием базового синтаксиса для создания имитатора и проверки вызова метода.

Основное применение PowerMock — возможность имитировать статические, окончательные и/или закрытые методы. Mockito2 теперь поддерживает имитирование окончательных классов/методов. Если вы используете Mockito2, рекомендуется использовать Mockito для имитирования окончательных методов/классов.

1. Зависимости PowerMock

Чтобы включить powermock в наше приложение, добавьте зависимости powermock-api-mockito2 и powermock-module-junit4. Обратите внимание, что официального расширения для JUnit 5 нет.

<dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>3.12.4</version><scope>test</scope></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-core</artifactId><version>2.0.9</version><scope>test</scope></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-api-mockito2</artifactId><version>2.0.9</version><scope>test</scope></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-module-junit4</artifactId><version>2.0.9</version><scope>test</scope></dependency>

Если вы планируете использовать его модуль отражения, например, вызывая закрытые методы, то нам также необходимо импортировать модуль powermock-reflect.

<dependency><groupId>org.powermock</groupId><artifactId>powermock-reflect</artifactId><version>2.0.9</version><scope>test</scope></dependency>

Важно включить совместимые версии Mockito и PowerMock, чтобы избежать возможных проблем во время выполнения.

2. Тестируемая система

Мы создаем простой класс, который имеет один частный метод, один финальный метод и один статический метод. Чтобы исключить ненужную сложность, мы просто возвращаем строковое значение из этих методов. Мы будем имитировать и тестировать эти методы.

public class Service {private String privateMessage() {return "Hello World!";}public static String staticMessage() {return "Hello World!";}public final String finalMessage() {return "Hello World!";}}

3. Подготовка расширения PowerMockito

PowerMockito — это API расширения PowerMock для поддержки Mockito. PowerMockito использует Java Reflection API mock final, static или private методы, чтобы помочь Mockito запускать тесты с использованием этих методов.

Для подготовки к тестам мы применяем две аннотации на уровне тестового класса. Здесь класс Service содержит методы, которые нужно имитировать.

@RunWith(PowerMockRunner.class)@PrepareForTest( { Service.class })public class AppTest {...}

В аннотации @PrepareForTest мы можем передавать полные имена типов, которые мы хотим имитировать. Например, в данном объявлении PowerMockito подготовит все классы в указанном пакете для имитации.

@PrepareForTest(fullyQualifiedNames = "com.howtodoinjava.demo.service.*")

4. Имитация статического метода

Для имитации статических методов PowerMock предлагает два подхода:

  • Используйте PowerMockito.mockStatic() для создания макета статического класса или всех статических методов в классе.
  • Используйте PowerMockito.spy() для имитации определенного статического метода.
PowerMockito.mockStatic(Static.class);Mockito.when(Static.staticMethod(paramIfAny)).thenReturn(value);//orStatic spy = PowerMockito.spy(new Static());PowerMockito.when(spy.staticMethod(paramIfAny)).thenReturn(value);

Для демонстрации мы будем имитировать метод Service.staticMessage(), который является статическим методом в классе Service.

@Testpublic void mockStaticMethodTest() {//Mock static methodsPowerMockito.mockStatic(Service.class);//Set expectationMockito.when(Service.staticMessage()).thenReturn("New Message from Mock!");//invoke the methodString message = Service.staticMessage();//Assert the stub responseAssert.assertEquals(message, "New Message from Mock!");}

Чтобы проверить вызов статического метода, сначала вызовите PowerMockito.verifyStatic(Static.class) для начала проверки поведения, а затем вызовите фактический статический метод для проверки. Важно отметить, что нам нужно вызывать verifyStatic() для каждой проверки статического метода.

PowerMockito.verifyStatic(Service.class);Service.staticMessage();

Чтобы проверить количество вызовов метода, мы можем передать Mockito.VerificationMode методу verifyStatic().

PowerMockito.verifyStatic(Service.class, Mockito.times(1));Service.staticMessage();

5. Имитация финального метода

Мокапирование окончательных методов похоже на статические методы, за исключением того, что нам нужно использовать PowerMockito.mock(class) вместо метода mockStatic().

 @Testpublic void mockFinalMethodTest() {//Mock final methodService serviceMock = PowerMockito.mock(Service.class);//Set expectationMockito.when(serviceMock.finalMessage()).thenReturn("New Message from Mock!");//invoke the methodString message = serviceMock.finalMessage();//Assert the stub responseAssert.assertEquals(message, "New Message from Mock!");}

Для проверки окончательных вызовов методов мы можем использовать метод Mockito.verify().

 //Verify final method invocationMockito.verify(serviceMock).finalMessage();

6. Имитация закрытого метода

Для имитации приватных методов мы будем использовать частичный фиктивный метод spy(). Также мы используем API WhiteBox для выполнения приватного метода в классе.

 @Testpublic void mockPrivateMethodTest() throws Exception {Service mock = PowerMockito.spy(new Service());PowerMockito.doReturn("New Message from Mock!").when(mock,"privateMessage");String privateMessage = Whitebox.invokeMethod(mock, "privateMessage");Assert.assertEquals(privateMessage, "New Message from Mock!");}

Используйте метод PowerMockito.verifyPrivate() для проверки вызовов приватных методов.

PowerMockito.verifyPrivate(mock, times(1)).invoke("privateMessage");

7. Включить подробное ведение журнала

Эти настройки фиктивных объектов используются редко, но могут быть полезны в некоторых случаях. Используйте их, если вы хотите назвать фиктивные объекты для будущих целей отладки или если вы хотите включить подробное ведение журнала для получения дополнительной информации.

@Testpublic void mockFinalMethodTest() {//Mock final methodService serviceMock = PowerMockito.mock(Service.class, Mockito.withSettings().name("ServiceMock").verboseLogging());//Set expectationMockito.when(serviceMock.finalMessage()).thenReturn("New Message from Mock!");//invoke the methodString message = serviceMock.finalMessage();//Assert the stub responseAssert.assertEquals(message, "New Message from Mock!");//Verify final method invocationMockito.verify(serviceMock).finalMessage();}

Запустите тест выше, чтобы получить следующий результат в консоли. finalMessage() был упомянут в 3 местах в тесте:

  • При установке ожидания возвращается ноль.
  • При вызове на фиктивном объекте возвращается заглушенный ответ.
  • При проверке возвращается ноль.
############ Logging method invocation #1 on mock/spy ########ServiceMock.finalMessage();invoked: -> at com.howtodoinjava.demo.powermock.PowerMockTests.mockFinalMethodTest(PowerMockTests.java:46)has returned: "null"############ Logging method invocation #2 on mock/spy ########stubbed: -> at com.howtodoinjava.demo.powermock.PowerMockTests.mockFinalMethodTest(PowerMockTests.java:46)ServiceMock.finalMessage();invoked: -> at com.howtodoinjava.demo.powermock.PowerMockTests.mockFinalMethodTest(PowerMockTests.java:50)has returned: "New Message from Mock!"(java.lang.String)############ Logging method invocation #3 on mock/spy ########ServiceMock.finalMessage();invoked: -> at com.howtodoinjava.demo.powermock.PowerMockTests.mockFinalMethodTest(PowerMockTests.java:56)has returned: "null"

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

В этом уроке по powermock мы научились настраивать powermock с помощью mockito и JUnit. Мы научились имитировать и заглушать частные, статические и финальные методы в тестируемом классе. Наконец, мы научились проверять вызовы методов и количество вызовов, включая подробное ведение журнала.

Исходный код на Github

Прокрутить вверх