我有两个类来弄清楚 whenNew
的工作原理。
public class RockService {
public RockData serv() {
RockData rockData = new RockData();
rockData.setName("RockService");
rockData.setContent("content from rock service");
return rockData;
} }
和
public class RockData {
String name;
long id;
String content;
// get set method ignored
}
使用测试代码
@RunWith(PowerMockRunner.class)
@PrepareForTest(RockService.class)
public class MockNewInstanceCreation {
@Test
public void mockCreationTest() throws Exception {
RockData rockData = mock(RockData.class);
when(rockData.getName()).thenReturn("this is mock");
whenNew(RockData.class).withNoArguments().thenReturn(rockData);
RockService rockService = new RockService();
RockData servData = rockService.serv();
System.out.println(servData.getName());
System.out.println(servData.getContent());
}
}
因此,在运行时,如果不是模拟,则输出(RockData's getName())
将是"摇滚服务"。但是有了模拟,它返回"这是模拟"。该代码有效,但我仍然不知道PowerMock/Mockito是如何做到这一点的。
我调试了代码。在执行RockData rockData = new RockData();
之后,我感到困惑的是,实际创建的完全是RockData rockData = mock(RockData.class);
创建的实例。这意味着new RockData()
根本不会创建新实例。它刚刚返回了已经创建的实例。在调试时,它跳到MockGateway.newInstanceCall
。
那么PowerMockito如何拦截新实例?
powermockrunner使用特殊类加载程序 - org.powermock.core.classloader.mockclassloader 进行测试。
而不是加载真实类,而是加载具有相同签名的新类别。这意味着不会调用真正的构造函数。
因此,新操作员返回的对象不是模拟。它是可以分配给真实类别的不同类的实例,并返回模拟的值。
请参阅下面的代码:
public class RockService {
ClassLoader classLoader = this.getClass().getClassLoader();
System.out.println("Real construct");
//Different class loader
System.out.println(classLoader);
//The same class
System.out.println(this.getClass());
}
public RockData serv() {
RockData rockData = new RockData();
Class<? extends RockData> clazz = rockData.getClass();
//This is a different class
System.out.println("Mocked class: " + clazz.getCanonicalName());
//And different classloader
ClassLoader classLoader = clazz.getClassLoader();
System.out.println(classLoader);
//Mocked class instance could be assigned to real one
System.out.println(RockData.class.isAssignableFrom(clazz));
//it's instance of both RockData.class and mocked class
System.out.println(clazz.isInstance(rockData));
System.out.println(RockData.class.isInstance(rockData));
rockData.setName("RockService");
rockData.setContent("content from rock service");
return rockData;
}