Unit testing 更换PowerMock';s@PrepareForest是否以编程方式进行?

Unit testing 更换PowerMock';s@PrepareForest是否以编程方式进行?,unit-testing,junit,powermock,Unit Testing,Junit,Powermock,我在junit测试中使用PowerMock模拟静态方法,通常如下所示: @RunWith(PowerMockRunner.class) @PrepareForTest({Foo.class,Bar.class}) 公共类单元测试{ @以前 公共测试(){ setUpFoo(); 设置栏(); } 私有void setUpFoo(){ mockStatic(Foo.class); when(Foo.someStaticMethod())。然后返回(1); } 专用void setUpBar(){

我在junit测试中使用PowerMock模拟静态方法,通常如下所示:

@RunWith(PowerMockRunner.class)
@PrepareForTest({Foo.class,Bar.class})
公共类单元测试{
@以前
公共测试(){
setUpFoo();
设置栏();
}
私有void setUpFoo(){
mockStatic(Foo.class);
when(Foo.someStaticMethod())。然后返回(1);
}
专用void setUpBar(){
mockStatic(Bar.class);
when(Bar.someStaticMethod())。然后返回(2);
}
@试验
public void someTestCase(){
...
}
}
这很好,但我发现指定
@PrepareForTest
注释会妨碍我使测试API变得灵活

我想做的事情如下:

公共类MockLibraryOne{
公共静态无效设置LibraryOne(){
setUpFoo();
设置栏();
}
私有静态void setUpFoo(){
mockStatic(Foo.class);
when(Foo.someStaticMethod())。然后返回(1);
}
私有静态void setUpBar(){
mockStatic(Bar.class);
when(Bar.someStaticMethod())。然后返回(2);
}
}
@RunWith(PowerMockRunner.class)
公共类单元测试{
@以前
公共测试(){
MockLibraryOne.setUpLibraryOne();
}
@试验
public void someTestCase(){
...
}
}
这里,我的单元测试依赖于
LibraryOne
,但它不知道
LibraryOne
依赖哪些类,因此它不知道要添加到
@PrepareForTest
注释中的类

我可以制作
SomeUnitTest
扩展
MockLibraryOne
,并将
@PrepareForTest
注释添加到
MockLibraryOne
类中,但在其他单元测试中,我的依赖性不仅仅是
MockLibraryOne
,因此继承不是一个通用的解决方案

是否有某种方法可以通过编程来准备一个类,以便在PowerMock下进行测试,而不是使用
@PrepareForTest
注释?例如,如下所示:

公共类MockLibraryOne{
公共静态无效设置LibraryOne(){
setUpFoo();
设置栏();
}
私有静态void setUpFoo(){
准备考试(食品类);
mockStatic(Foo.class);
when(Foo.someStaticMethod())。然后返回(1);
}
私有静态void setUpBar(){
准备测试(酒吧级别);
mockStatic(Bar.class);
when(Bar.someStaticMethod())。然后返回(2);
}
}
如果
PowerMockRunner
@PrepareForTest
注释的处理有点不同,我想这会很好:对于每个指定的类,它不仅应该将该类(及其层次结构)添加到类列表中以准备模拟,然后检查该类,看看它是否也有
@PrepareForTest
注释:

@RunWith(PowerMockRunner.class)
@PrepareForTest({MockLibraryOne.class})
公共类单元测试{
...
}
@PrepareForTest({Foo.class,Bar.class})
公共类MockLibraryOne{
...
}
}

因此在这里,
SomeUnitTest
上的
@PrepareForTest
注释将找到
MockLibraryOne
,那里的
@PrepareForTest
注释也将拖入
Foo.class
Bar.class

因此,也许编写我自己的测试运行程序来替换
PowerMockRunner
可能是一个解决方案

或者有一个更简单的解决方案,例如使用
PowerMockAgent

编辑:模拟策略可能是一种解决方案:


编辑:Mock策略与
PowerMockRunner
配合使用,但与
PowerMockRule
配合使用(似乎)不起作用(由于类加载器问题,我有时需要这样做)。

也许您正在寻找一个?

为什么要模拟静态方法?为什么不将这些静态方法封装在一个可以用mockito模拟的类中呢

class FooWraper {
   void someMethod() {
     Foo.someStaticMethod()
   }
}
然后,您可以创建一个Foowrapper的模拟。根本不需要使用Powermock…

您能帮忙吗(摘自文档)

您还可以使用通配符准备整个测试包:

@PrepareForTest(fullyQualifiedNames=“com.mypackage.*”)


因此,您可以将整个库添加到您的prepare…

您试图实现的目标将不起作用

问题是powermock必须重写客户机类的代码以拦截静态调用,而在类加载后它不能这样做。因此,它只能在加载类之前为测试准备类

假设您想在下面的简单类中模拟
System.currentTimeMillis
调用

class SystemClock {
    public long getTime() {
        return System.currentTimeMillis();
    }
}
Powermock不会更改java.lang.System.currentTimeMillis的代码,因为它不能更改。相反,它会更改
SystemClock
的字节码,以便不再调用
System.currentTimeMillis
。相反,它调用属于powermock的其他对象

这就是powermock get完全控制返回值的方式,并允许您编写如下测试:

@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
方法是如何处理的


谢谢您的回复。我调查了
MockPolicy
,但如上所述,它只适用于
PowerMockRunner
,而不适用于
PowerMockRule
。由于类加载器问题,我需要使用
PowerMockRule
,而
MockPolicy
初始值设定项仅适用于
PowerMockRunner
提供的
MockClassLoader
。我面临同样的问题。你找到解决办法了吗?找到了吗