我最近学习使用 PowerMock 为一个名为 Module
的类编写单元测试,该类扩展了 Base
类。它们看起来像这样。
public class Base {
protected final static ServiceA serviceA;
protected final static ServiceB serviceB;
static {
serviceA = ServiceA.getInstance();
serviceB = ServiceB.getInstance();
}
}
public class Module extends Base {
public DataA methodA() {
return serviceA.getDataA();
}
public DataB methodB() {
return serviceB.getDataB();
}
}
我的单元测试如下所示:
@PowerMockIgnore("javax.management.*")
@RunWith(PowerMockRunner.class)
@PrepareForTest({Module.class, ServiceA.class, ServiceB.class})
public class ModuleTest {
private Module module;
@Mock
private ServiceA serviceA;
@Mock
private ServiceB serviceB;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.mockStatic(ServiceA.class);
PowerMockito.when(ServiceA.getInstance).thenReturn(serviceA);
PowerMockito.mockStatic(ServiceB.class);
PowerMockito.when(ServiceB.getInstance).thenReturn(serviceB);
module = new Module();
// I spy it because it has other methods I need to mock
module = PowerMockito.spy(module);
}
@Test
public void methodATest() {
DataA dataA = new DataA();
PowerMockito.when(serviceA.getDataA()).thenReturn(dataA);
DataA data = module.methodA();
assertEquals(dataA, data);
}
@Test
public void methodBTest() {
DataB dataB = new DataB();
PowerMockito.when(serviceB.getDataB()).thenReturn(dataB);
DataB data = module.methodB();
assertEquals(dataB, data);
}
}
一切看起来都很简单,但是当我运行 ModuleTest
时,methodBTest()
没有通过。似乎 PowerMockito.when(serviceB.getDataB()).thenReturn(dataB) 不起作用并调用真正的 serviceB.getDataB() 方法。因此 assertEquals(dataB, data)
抛出 org.junit.ComparisonFailure
。
如果我将 methodBTest()
放在 methodATest()
之前,则 methodATest()
不会通过。同样的原因。
如果我放置 PowerMockito.when(serviceA.getDataA()).thenReturn(dataA)
和 PowerMockito.when(serviceB.getDataB()).thenReturn(dataB)
在 setup() 中,一切都运行良好。
这整天都与我接壤。有谁知道为什么会发生这种情况以及如何解决它?我需要在各自的测试方法中编写模拟语句,因为我可能会更改返回值。
最佳答案
这是一个(几乎)不进行任何更改的解决方案
@PowerMockIgnore("javax.management.*")
@RunWith(PowerMockRunner.class)
@PrepareForTest({Module.class, ServiceA.class, ServiceB.class})
public class ModuleTest {
private Module module;
private static ServiceA serviceA = Mockito.mock(ServiceA.class);
private static ServiceB serviceB = Mockito.mock(ServiceB.class);
@BeforeClass
public static void oneTimeSetup() throws Exception {
PowerMockito.mockStatic(ServiceA.class);
PowerMockito.when(ServiceA.class, "getInstance").thenReturn(serviceA);
PowerMockito.mockStatic(ServiceB.class);
PowerMockito.when(ServiceB.class, "getInstance").thenReturn(serviceB);
}
@Before
public void setup() throws Exception {
module = new Module();
// I spy it because it has other methods I need to mock
module = PowerMockito.spy(module);
}
@Test
public void methodATest() {
DataA dataA = new DataA();
Mockito.when(serviceA.getDataA()).thenReturn(dataA);
DataA data = module.methodA();
assertEquals(dataA, data);
}
@Test
public void methodBTest() {
DataB dataB = new DataB();
Mockito.when(serviceB.getDataB()).thenReturn(dataB);
DataB data = module.methodB();
assertEquals(dataB, data);
}
}
更改内容(以及原因):
- 在
Base
:serviceA
和serviceB
更改为 protected (Module
如果私有(private)则无法访问) - 对
PowerMockito.when(ServiceA.class, "getInstance").thenReturn(serviceA);
使用了“正确的”(据我所知)语法 - 使用了
@BeforeClass
并制作serviceA
和serviceB
static 来“绕过”Base
中的静态初始化
使用 Junit 4.12、PowerMockito 1.6.2 进行测试。
<小时/>注意:也可以利用@SuppressStaticInitializationFor
为了达到同样的目标:
@SuppressStaticInitializationFor(value = "so46196071.Base") // suppress the static in Base (note this is my package name)
@PowerMockIgnore("javax.management.*")
@RunWith(PowerMockRunner.class)
@PrepareForTest({Module.class, ServiceA.class, ServiceB.class})
public class ModuleBisTest {
private Module module;
@Mock
private ServiceA serviceA;
@Mock
private ServiceB serviceB;
@Before
public void setup() throws Exception {
// MockitoAnnotations.initMocks(this); /* this is not needed => done by the runner */
PowerMockito.mockStatic(ServiceA.class);
PowerMockito.when(ServiceA.class, "getInstance").thenReturn(serviceA);
PowerMockito.mockStatic(ServiceB.class);
PowerMockito.when(ServiceB.class, "getInstance").thenReturn(serviceB);
module = new Module();
Whitebox.setInternalState(Base.class, "serviceA", serviceA); // set serviceA in Base "by hand"
Whitebox.setInternalState(Base.class, "serviceB", serviceB); // set serviceB in Base "by hand"
// I spy it because it has other methods I need to mock
module = PowerMockito.spy(module);
}
// ...
关于java - 使用PowerMock编写单元测试,mocked方法调用失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46196071/