На хабре уже была статья с примерами использования PowerMock, но в ней не хватает такого описания, как имитации вызова статических методов как самостоятельных «единиц» в классе, так и в гибридном использовании, когда часть статических методов у класса подменяются «заглушкой», а часть вызываются реально. Попробую исправить эту нишу.
Для начала создадим демонстрационный класс со статическими методами (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, так как он нам не требовался. Теперь же, чтобы сделать «заглушку» для статических методов, создадим «костяк» тестового класса:
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();
}
}
Мы создали класс ClassStaticMockTest, в котором переопределили Object Factory для TestNG (документация)
Добавим тест с «заглушкой» для статического метода:
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: добавил пример имитации вызова статических методов при тестировании «обычных» классов.
Автор: Borz