powermockito如何拦截新实例?

时间:2016-06-24 05:05:27

标签: mockito classloader powermockito

我有两个课程试图找出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"。但是通过模拟,它返回"这是模拟"。该代码有效,但我仍然不知道Powermock / Mockito究竟是如何做到这一点的。

我调试了代码。困扰我的是在执行RockData rockData = new RockData();之后,实际创建的内容正是由RockData rockData = mock(RockData.class);创建的实例。这意味着new RockData()根本不会创建新实例。它只返回了一个已创建的实例。在调试时,它跳转到MockGateway.newInstanceCall

那么Powermockito如何拦截新实例?

1 个答案:

答案 0 :(得分:0)

PowerMockRunner使用特殊的类加载器运行测试 - org.powermock.core.classloader.MockClassLoader

不是加载真正的类,而是加载一个具有相同签名的新类。这意味着不会调用真正的构造函数。

因此new运算符返回的对象不是Mock。它是可以分配给真实类的不同类的实例,它返回模拟值。

请参阅以下代码:

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;
}