На хабре уже была статья с примерами использования PowerMock, но в ней не хватает такого описания, как имитации вызова статических методов как самостоятельных «единиц» в классе, так и в гибридном использовании, когда часть статических методов у класса подменяются «заглушкой», а часть вызываются реально. Попробую исправить эту нишу.
Для начала создадим демонстрационный класс со статическими методами (commit):
Добавим простое тестирование статичных методов. Для этого создадим класс ClassStaticTest (commit):
До этого момента мы ни разу не использовали PowerMock, так как он нам не требовался. Теперь же, чтобы сделать «заглушку» для статических методов, создадим «костяк» тестового класса:
Добавим тест с «заглушкой» для статического метода:
Остался заключительный «аккорд» — гибридный тест, в котором вызов одного статического метод перекрыт «заглушкой», а для другого делается реальный вызов (commit):
Как «жизненный» пример потребности использования имитации вызова статических методов является «перекрытие» данных методов «заглушками» при тестировании других классов/методов (commit).
Добавляем класс, использующий у себя статический метод
И добавляем тест для метода
PS: Все примеры можно найти на GitHub.
UPD: добавил пример имитации вызова статических методов при тестировании «обычных» классов.
Для начала создадим демонстрационный класс со статическими методами (commit):
public class ClassStatic { static String getValue() { return "value"; } static String getValue(final String s) { return getValue() + s; } }
Добавим простое тестирование статичных методов. Для этого создадим класс ClassStaticTest (commit):
import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @Test(groups = {"static", "noMock"}) public class ClassStaticTest { @DataProvider public Object[][] data() throws Exception { return new Object[][]{{"", "value"}, {"test", "valuetest"}}; } @Test public void testGetValueVoid() throws Exception { Assert.assertEquals(ClassStatic.getValue(), "value"); } @Test(dataProvider = "data", dependsOnMethods = {"testGetValueVoid"}) public void testGetValueAttribute(final String v, final String r) throws Exception { Assert.assertEquals(ClassStatic.getValue(v), r); } }
До этого момента мы ни разу не использовали PowerMock, так как он нам не требовался. Теперь же, чтобы сделать «заглушку» для статических методов, создадим «костяк» тестового класса:
Мы создали класс ClassStaticMockTest, в котором переопределили Object Factory для TestNG (документация)import org.powermock.modules.testng.PowerMockObjectFactory; import org.testng.IObjectFactory; import org.testng.annotations.ObjectFactory; import org.testng.annotations.Test; @Test(groups = {"static", "mock"}) public class ClassStaticMockTest { @ObjectFactory public IObjectFactory setObjectFactory() { return new PowerMockObjectFactory(); } }
Добавим тест с «заглушкой» для статического метода:
import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.testng.PowerMockObjectFactory; import org.testng.Assert; import org.testng.IObjectFactory; import org.testng.annotations.DataProvider; import org.testng.annotations.ObjectFactory; import org.testng.annotations.Test; import static org.mockito.MockitoAnnotations.Mock; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.when; @Test(groups = {"static", "mock"}) @PrepareForTest({ClassStatic.class}) public class ClassStaticMockTest { @Mock public ClassStatic classStatic; @DataProvider public Object[][] data() throws Exception { return new Object[][]{{"", "newValue"}, {"test", "newValuetest"}}; } @ObjectFactory public IObjectFactory setObjectFactory() { return new PowerMockObjectFactory(); } @Test(dependsOnGroups = {"noMock"}) public void mockGetValueVoid() throws Exception { mockStatic(ClassStatic.class); when(ClassStatic.getValue()).thenReturn("newValue"); Assert.assertEquals(ClassStatic.getValue(), "newValue"); } }
Остался заключительный «аккорд» — гибридный тест, в котором вызов одного статического метод перекрыт «заглушкой», а для другого делается реальный вызов (commit):
Мы перекрываем вызов методаimport org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.testng.PowerMockObjectFactory; import org.testng.Assert; import org.testng.IObjectFactory; import org.testng.annotations.DataProvider; import org.testng.annotations.ObjectFactory; import org.testng.annotations.Test; import static org.mockito.MockitoAnnotations.Mock; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.when; /** Created by borz on 06.07.13. */ @Test(groups = {"static", "mock"}) @PrepareForTest({ClassStatic.class}) public class ClassStaticMockTest { @Mock public ClassStatic classStatic; @DataProvider public Object[][] data() throws Exception { return new Object[][]{{"", "newValue"}, {"test", "newValuetest"}}; } @ObjectFactory public IObjectFactory setObjectFactory() { return new PowerMockObjectFactory(); } @Test(dependsOnGroups = {"noMock"}) public void mockGetValueVoid() throws Exception { mockStatic(ClassStatic.class); when(ClassStatic.getValue()).thenReturn("newValue"); Assert.assertEquals(ClassStatic.getValue(), "newValue"); } @Test(dataProvider = "data", dependsOnMethods = {"mockGetValueVoid"}) public void testGetValueAttribute(final String v, final String r) throws Exception { mockStatic(ClassStatic.class); when(ClassStatic.getValue()).thenReturn("newValue"); when(ClassStatic.getValue(v)).thenCallRealMethod(); Assert.assertEquals(ClassStatic.getValue(v), r); } }
getValue() и указываем, что при вызове getValue(String value) будет вызван реальный метод у класса, который уже внутри себя вызывает getValue().Как «жизненный» пример потребности использования имитации вызова статических методов является «перекрытие» данных методов «заглушками» при тестировании других классов/методов (commit).
Добавляем класс, использующий у себя статический метод
ClassStatic.getValue():public class ClassUseStatic { public String getValue() { return ClassStatic.getValue() + "noStatic"; } }
И добавляем тест для метода
ClassUseStatic.getValue() с переопределением вызова ClassStatic.getValue():import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.testng.PowerMockObjectFactory; import org.testng.Assert; import org.testng.IObjectFactory; import org.testng.annotations.DataProvider; import org.testng.annotations.ObjectFactory; import org.testng.annotations.Test; import static org.mockito.MockitoAnnotations.Mock; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.when; @Test(groups = {"useStatic", "mock"}, dependsOnGroups = {"static"}) @PrepareForTest({ClassStatic.class}) public class ClassUseStaticTest { @Mock public ClassStatic classStatic; @DataProvider public Object[][] data() throws Exception { return new Object[][]{{"newValue1", "newValue1noStatic"}, {"newValue2", "newValue2noStatic"}}; } @ObjectFactory public IObjectFactory setObjectFactory() { return new PowerMockObjectFactory(); } @Test(dataProvider = "data") public void testGetValue(String value, String result) throws Exception { mockStatic(ClassStatic.class); when(ClassStatic.getValue()).thenReturn(value); ClassUseStatic testClass = new ClassUseStatic(); Assert.assertEquals(result, testClass.getValue()); } }
PS: Все примеры можно найти на GitHub.
UPD: добавил пример имитации вызова статических методов при тестировании «обычных» классов.
