powermockito如何拦截新实例?
How powermockito intercept new instance?
我有两个 类 试图弄清楚 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());
}
}
所以在运行时,如果不是 mock,输出 (RockData's getName())
将是 "RockService"。但是对于 mock,它 returns "this is mock"。该代码有效,但我仍然不知道 Powermock/Mockito 究竟是如何做到这一点的。
我调试了代码。令我困惑的是RockData rockData = new RockData();
执行后,实际创建的正是RockData rockData = mock(RockData.class);
创建的实例。这意味着 new RockData()
根本不会创建新实例。它只是返回一个已经创建的实例。而且调试的时候跳到MockGateway.newInstanceCall
.
那么Powermockito是如何拦截新实例的呢?
PowerMockRunner 使用特殊的 class 加载程序运行测试 - org.powermock.core.classloader.MockClassLoader。
它没有加载真正的 class,而是加载了一个具有相同签名的新的。这意味着不会调用真正的构造函数。
所以new操作符返回的对象不是Mock。它是一个不同的 class 的实例,可以分配给真实的 returns 模拟值。
查看下面的代码:
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;
}
我有两个 类 试图弄清楚 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());
}
}
所以在运行时,如果不是 mock,输出 (RockData's getName())
将是 "RockService"。但是对于 mock,它 returns "this is mock"。该代码有效,但我仍然不知道 Powermock/Mockito 究竟是如何做到这一点的。
我调试了代码。令我困惑的是RockData rockData = new RockData();
执行后,实际创建的正是RockData rockData = mock(RockData.class);
创建的实例。这意味着 new RockData()
根本不会创建新实例。它只是返回一个已经创建的实例。而且调试的时候跳到MockGateway.newInstanceCall
.
那么Powermockito是如何拦截新实例的呢?
PowerMockRunner 使用特殊的 class 加载程序运行测试 - org.powermock.core.classloader.MockClassLoader。
它没有加载真正的 class,而是加载了一个具有相同签名的新的。这意味着不会调用真正的构造函数。
所以new操作符返回的对象不是Mock。它是一个不同的 class 的实例,可以分配给真实的 returns 模拟值。
查看下面的代码:
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;
}