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. Мы научились имитировать и заглушать частные, статические и финальные методы в тестируемом классе. Наконец, мы научились проверять вызовы методов и количество вызовов, включая подробное ведение журнала.