当前位置: 首页 > 工具软件 > PowerMock > 使用案例 >

PowerMock

赵佐
2023-12-01

EasyMock 以及 Mockito 都因为可以极大地简化单元测试的书写过程而被许多人应用在自己的工作中,但是这 2 种 Mock 工具都不可以实现对静态函数、构造函数、私有函数、Final 函数以及系统函数的模拟,但是这些方法往往是我们在大型系统中需要的功能。PowerMock 是在 EasyMock 以及 Mockito 基础上的扩展,通过定制类加载器等技术,PowerMock 实现了之前提到的所有模拟功能,使其成为大型系统上单元测试中的必备工具。

PowerMock 也是一个单元测试模拟框架,它是在其它单元测试模拟框架的基础上做出的扩展。通过提供定制的类加载器以及一些字节码篡改技巧的应用,PowerMock 现了对静态方法、构造方法、私有方法以及 Final 方法的模拟支持,对静态初始化过程的移除等强大的功能。因为 PowerMock 在扩展功能时完全采用和被扩展的框架相同的 API, 熟悉 PowerMock 所支持的模拟框架的开发者会发现 PowerMock 非常容易上手。PowerMock 的目的就是在当前已经被大家所熟悉的接口上通过添加极少的方法和注释来实现额外的功能,目前,PowerMock 仅支持 EasyMock 和 Mockito。

PowerMock简单实现原理

当某个测试方法被注解@PrepareForTest标注以后,在运行测试用例时,会创建一个新的org.powermock.core.classloader.MockClassLoader实例,然后加载该测试用例使用到的类(系统类除外)。

PowerMock会根据你的mock要求,去修改写在注解@PrepareForTest里的class文件(当前测试类会自动加入注解中),以满足特殊的mock需求。例如:去除final方法的final标识,在静态方法的最前面加入自己的虚拟实现等。

如果需要mock的是系统类的final方法和静态方法,PowerMock不会直接修改系统类的class文件,而是修改调用系统类的class文件,以满足mock需求。

PowerMock有两个重要的注解

  • @RunWith(PowerMockRunner.class)
  • @PrepareForTest( { YourClassWithEgStaticMethod.class })

当PowerMock普通用法时可以不用加这两个注解。当你需要使用PowerMock强大功能(Mock静态、final、私有方法等)的时候,就需要加这两个注解。@RunWith只能加在类上,@PrepareForTest可以加在类或者方法上。

Maven依赖

<dependency>
    <groupId>org.powermock</groupId>
   <artifactId>powermock-api-mockito</artifactId>
   <version>1.6.6</version>
 </dependency>
 <dependency>
   <groupId>org.powermock</groupId>
   <artifactId>powermock-module-junit4</artifactId>
   <version>1.6.6</version>
   <scope>test</scope>
 </dependency>

特有方法

方法说明
PowerMockito.whenNew(type).withArguments().thenReturn(Object)对new 出来的对象的模拟操作
PowerMockito.when(method).thenReturn(Object)对静态方法、构造方法、私有方法以及 Final 方法的模拟操作
PowerMockito.mockStatic(class)Mock类的所有静态方法

实列

普通Mock: Mock参数传递的对象
说明:普通Mock不需要加@RunWith和@PrepareForTest注解。

@Test
public void testCallArgumentInstance(){
    File mock = PowerMockito.mock(File.class);
    Source source = new Source();
    PowerMockito.when(mock.exists()).thenReturn(true);
    Assert.assertTrue(source.callArgumentInstance(mock));
}
class Source {
    public boolean callArgumentInstance(File file) {
        return file.exists();
    }
}

Mock方法内部new出来的对象
说明:当使用PowerMockito.whenNew方法时,必须加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是需要mock的new对象代码所在的类。

@RunWith(PowerMockRunner.class)
public class UserTest {
    @Test
    @PrepareForTest(Source.class)
    public void testCallInternalInstance() throws Exception
    {
        File mock = PowerMockito.mock(File.class);
        Source source = new Source();
        PowerMockito.whenNew(File.class).withArguments("bbb").thenReturn(mock);
        PowerMockito.when(mock.exists()).thenReturn(true);
        Assert.assertTrue(source.callInternalInstance("bbb"));
    }
    class Source {
        public boolean callInternalInstance(String path) {
            File file = new File(path);
            return file.exists();
        }
    }
}

Mock普通对象的final方法
说明: 当需要mock final方法的时候,必须加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是final方法所在的类

@RunWith(PowerMockRunner.class)
public class UserTest {
    @Test
    @PrepareForTest(SourceDepend.class)
    public void testCallFinalMethod() throws Exception
    {
        SourceDepend mock = PowerMockito.mock(SourceDepend.class);
        Source source = new Source();
        PowerMockito.when(mock.isAlive()).thenReturn(true);
        Assert.assertTrue(source.callFinalMethod(mock));
    }
    class Source {
        public boolean callFinalMethod(SourceDepend refer) {
            return refer.isAlive();
        }
    }
    class SourceDepend {
        public final boolean isAlive() {
            return false;
        }
    }
}

Mock普通类的静态方法
说明:当需要mock静态方法的时候,必须加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是静态方法所在的类

@RunWith(PowerMockRunner.class)
public class UserTest {
    @Test
    @PrepareForTest(Source.class)
    public void testCallStaticMethod() {
        Source source = new Source();
        PowerMockito.mockStatic(Source.class);
        PowerMockito.when(Source.isExist()).thenReturn(true);
        Assert.assertTrue(source.callStaticMethod());
    }
    static class Source {
        public boolean callStaticMethod() {
            return Source.isExist();
        }
        public static boolean isExist()
        {
            return false;
        }
    }
}

Mock 私有方法
说明:和Mock普通方法一样,只是需要加注解@PrepareForTest(ClassUnderTest.class),注解里写的类是私有方法所在的类。

@RunWith(PowerMockRunner.class)
public class UserTest {
    @Test
    @PrepareForTest(Source.class)
    public void testCallPrivateMethod() throws Exception
    {
        Source source = PowerMockito.mock(Source.class);
        PowerMockito.when(source.callPrivateMethod()).thenCallRealMethod();
        PowerMockito.when(source, "isExist").thenReturn(true);
        Assert.assertTrue(source.callPrivateMethod());
    }
    static class Source {
        public boolean callPrivateMethod() {
            return isExist();
        }
        private boolean isExist() {
            return false;
        }
    }
}

Mock系统类的静态和final方法
说明:和Mock普通对象的静态方法、final方法一样,只不过注解@PrepareForTest里写的类不一样 ,注解里写的类是需要调用系统方法所在的类。

@RunWith(PowerMockRunner.class)
public class UserTest {
    @Test
    @PrepareForTest(Source.class)
    public void testCallSystemStaticMethod()
    {
        Source source = new Source();
        PowerMockito.mockStatic(System.class);
        PowerMockito.when(System.getProperty("aaa")).thenReturn("bbb");
        Assert.assertEquals("bbb", source.callSystemStaticMethod("aaa"));
    }
    static class Source {
        public String callSystemStaticMethod(String str)
        {
            return System.getProperty(str);
        }
    }
}

转载:https://www.cnblogs.com/hunterCecil/p/5721468.html

 类似资料: