我正在使用PowerMock在junit测试中模拟静态方法,通常按如下方式进行:
@RunWith(PowerMockRunner.class)
@PrepareForTest({Foo.class,Bar.class})
public class SomeUnitTest {
@Before
public void setUpTest() {
setUpFoo();
setUpBar();
}
private void setUpFoo() {
mockStatic(Foo.class);
when(Foo.someStaticMethod()).thenReturn(1);
}
private void setUpBar() {
mockStatic(Bar.class);
when(Bar.someStaticMethod()).thenReturn(2);
}
@Test
public void someTestCase() {
...
}
}
这很好用,但我发现指定@PrepareForTest
注释会阻止我使我的测试API变得灵活。
我想做的事情如下:
public class MockLibraryOne {
public static void setUpLibraryOne() {
setUpFoo();
setUpBar();
}
private static void setUpFoo() {
mockStatic(Foo.class);
when(Foo.someStaticMethod()).thenReturn(1);
}
private static void setUpBar() {
mockStatic(Bar.class);
when(Bar.someStaticMethod()).thenReturn(2);
}
}
@RunWith(PowerMockRunner.class)
public class SomeUnitTest {
@Before
public void setUpTest() {
MockLibraryOne.setUpLibraryOne();
}
@Test
public void someTestCase() {
...
}
}
这里我的单元测试依赖于LibraryOne
,但它不知道哪些类LibraryOne
依赖,所以它不知道要添加到@PrepareForTest
注释的类。
我可以SomeUnitTest
扩展MockLibraryOne
并将@PrepareForTest
注释添加到MockLibraryOne
类,但我将依赖于MockLibraryOne
以上的@PrepareForTest
其他单元测试,所以继承不是一般的解决方案。
是否有某种方法可以在PowerMock下以编程方式准备类进行测试,而不是使用public class MockLibraryOne {
public static void setUpLibraryOne() {
setUpFoo();
setUpBar();
}
private static void setUpFoo() {
prepareForTest(Foo.class);
mockStatic(Foo.class);
when(Foo.someStaticMethod()).thenReturn(1);
}
private static void setUpBar() {
prepareForTest(Bar.class);
mockStatic(Bar.class);
when(Bar.someStaticMethod()).thenReturn(2);
}
}
注释?例如,类似以下内容:
PowerMockRunner
我想如果@PrepareForTest
稍微不同地处理@PrepareForTest
注释会很好:对于每个指定的类,它不应该只将类(及其层次结构)添加到类列表中准备模拟,但然后检查该类,看它是否还有@RunWith(PowerMockRunner.class)
@PrepareForTest({MockLibraryOne.class})
public class SomeUnitTest {
...
}
@PrepareForTest({Foo.class,Bar.class})
public class MockLibraryOne {
...
}
注释:
@PrepareForTest
}
因此,SomeUnitTest
上的MockLibraryOne
注释会找到@PrepareForTest
,而Foo.class
注释会拖入Bar.class
和PowerMockRunner
同样。
因此,编写我自己的测试运行器以替换PowerMockAgent
可能是一种解决方案。
或许有一个更简单的解决方案,例如使用PowerMockRunner
类?
编辑:模拟策略可能是一个解决方案:https://code.google.com/p/powermock/wiki/MockPolicies
编辑:模拟策略适用于PowerMockRule
但不适用于{{1}}(我有时会因为类加载器问题而需要)。
答案 0 :(得分:0)
也许您正在寻找mock policy?
答案 1 :(得分:0)
为什么你甚至想模拟静态方法?为什么不将这些静态方法包装在可以用mockito模拟的类中?
class FooWraper {
void someMethod() {
Foo.someStaticMethod()
}
}
然后你可以创建一个你的FooWraper模拟器。根本不需要使用Powermock ......
答案 2 :(得分:0)
您能帮忙吗(摘自文档)?
您还可以使用通配符准备整个程序包以进行测试:
@PrepareForTest(fullyQualifiedNames =“ com.mypackage。*”)
因此您可以将整个库添加到您的准备中...
答案 3 :(得分:0)
您尝试实现的目标将无效。
问题在于powermock必须重写客户端类的代码以拦截静态调用,并且在类加载后无法执行此操作。因此,它只能在加载之前准备要测试的类。
假设您想在以下简单类中模拟System.currentTimeMillis
调用。
class SystemClock {
public long getTime() {
return System.currentTimeMillis();
}
}
Powermock不会更改java.lang.System.currentTimeMillis
的代码,因为它不能更改。相反,它将更改SystemClock
的字节码,以使其不再调用System.currentTimeMillis
。相反,它会调用属于powermock的其他对象。
这是powermock完全控制返回值的方式,并允许您编写如下测试:
@RunWith(PowerMockRunner.class)
@PrepareForTest({ SystemClock.class })
public class PowerMockitoTest {
@Test
public void systemTimeMillis() {
SystemClock systemClock = new SystemClock();
PowerMockito.mockStatic(System.class);
PowerMockito.when(System.currentTimeMillis()).thenReturn(12345L);
long time = systemClock.getTime();
assertEquals(12345L, time);
}
}
您可以看到powermock在调试器的stacktrace中重写了客户端类。在SystemClock.getTime
处设置一个断点,然后进入所调用的方法。
如您所见,SystemClock
会调用MockGateway
。
如果您查看MockGateway
调用堆栈中的变量,则可以看到原始System.currentTimeMillis
方法是如何处理的。