PowerMock简单实现原理

我们先来看看PowerMock的依赖:

PowerMock简单实现原理

可以看出来,它有两个重要的依赖:javassist和objenesis。
javassist是一个修改java字节码的工具包,objenesis是一个绕过构造方法来实例化一个对象的工具包。由此看来,PowerMock的本质是通过修改字节码来实现对静态和final等方法的mock的。

@RunWith(PowerMockRunner.class)
public class TestClassUnderTest {

    @Test
    public void testCallArgumentInstance() {
        showClassLoader("testCallArgumentInstance");
        File file = PowerMockito.mock(File.class);
        ClassUnderTest underTest = new ClassUnderTest();
        PowerMockito.when(file.exists()).thenReturn(true);
        Assert.assertTrue(underTest.callArgumentInstance(file));
    }
    
    @Test
    @PrepareForTest(ClassUnderTest.class)
    public void testCallInternalInstance() throws Exception {
        showClassLoader("testCallInternalInstance");
        File file = PowerMockito.mock(File.class);
        ClassUnderTest underTest = new ClassUnderTest();
        PowerMockito.whenNew(File.class).withArguments("bbb").thenReturn(file);
        PowerMockito.when(file.exists()).thenReturn(true);
        Assert.assertTrue(underTest.callInternalInstance("bbb"));
    }
    
    @Test
    @PrepareForTest(ClassDependency.class)
    public void testCallFinalMethod() {
        showClassLoader("testCallFinalMethod");
        ClassDependency depencency = PowerMockito.mock(ClassDependency.class);
        ClassUnderTest underTest = new ClassUnderTest();
        PowerMockito.when(depencency.isAlive()).thenReturn(true);
        Assert.assertTrue(underTest.callFinalMethod(depencency));
    }
    
    private void showClassLoader(String methodName) {
        System.out.println("=============="+methodName+"===============");
        System.out.println("TestClassUnderTest: " + TestClassUnderTest.class.getClassLoader());
        System.out.println("ClassUnderTest: " + ClassUnderTest.class.getClassLoader());
        System.out.println("ClassDependency: " + ClassDependency.class.getClassLoader());
    }
}


我们通过这段代码打印出来的ClassLoader,能让我们对PowerMock的实现原理有近一步的了解。
引用
==============testCallArgumentInstance===============
TestClassUnderTest: sun.misc.Launcher$ AppClassLoader@19821f
ClassUnderTest: sun.misc.Launcher$AppClassLoader@19821f
ClassDependency: sun.misc.Launcher$AppClassLoader@19821f
==============testCallInternalInstance===============
TestClassUnderTest: org.powermock.core.classloader .MockClassLoader@117a8bd
ClassUnderTest: org.powermock.core.classloader.MockClassLoader@117a8bd
ClassDependency: org.powermock.core.classloader.MockClassLoader@117a8bd
==============testCallFinalMethod===============
TestClassUnderTest: org.powermock.core.classloader. MockClassLoader@1f42b49
ClassUnderTest: org.powermock.core.classloader.MockClassLoader@1f42b49
ClassDependency: org.powermock.core.classloader.MockClassLoader@1f42b49


下面是我总结的PowerMock的简单实现原理:
  • 当某个测试方法被注解@PrepareForTest标注以后,在运行测试用例时,会创建一个新的org.powermock.core.classloader.MockClassLoader实例,然后加载该测试用例使用到的类(系统类除外)。
  • PowerMock会根据你的mock要求,去修改写在注解@PrepareForTest里的class文件(当前测试类会自动加入注解中),以满足特殊的mock需求。例如:去除final方法的final标识,在静态方法的最前面加入自己的虚拟实现等。
  • 如果需要mock的是系统类的final方法和静态方法,PowerMock不会直接修改系统类的class文件,而是修改调用系统类的class文件,以满足mock需求。


在没有看PowerMock源码之前,我一直很疑惑PowerMock是怎么mock系统类的final方法和静态方法的?难道它真的能修改了系统类的类文件,因为系统的类文件是被Boostarp ClassLoader加载的,应用程序无法修改它,所以就很好奇。看了源码之后,原来才发现PowerMock并没有修改系统类,而是修改了调用系统类的地方,这才豁然开朗它是如何mock系统类的行为的。


你可能感兴趣的:(java,Powermock,原理)