PowerMock教程(一)

  • Post author:
  • Post category:其他




1、背景介绍

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

Mockito 是一个针对 Java 的单元测试模拟框架,它与 EasyMock 和 jMock 很相似,都是为了简化单元测试过程中测试上下文 ( 或者称之为测试驱动函数以及桩函数 ) 的搭建而开发的工具。在有这些模拟框架之前,为了编写某一个函数的单元测试,程序员必须进行十分繁琐的初始化工作,以保证被测试函数中使用到的环境变量以及其他模块的接口能返回预期的值,有些时候为了单元测试的可行性,甚至需要牺牲被测代码本身的结构。单元测试模拟框架则极大的简化了单元测试的编写过程:在被测试代码需要调用某些接口的时候,直接模拟一个假的接口,并任意指定该接口的行为。这样就可以大大的提高单元测试的效率以及单元测试代码的可读性。

相对于 EasyMock 和 jMock,Mockito 的优点是通过在执行后校验哪些函数已经被调用,消除了对期望行为(expectations)的需要。其它的 mocking 库需要在执行前记录期望行为(expectations),而这导致了丑陋的初始化代码。

但是,Mockito 也并不是完美的,它不提供对静态方法、构造方法、私有方法以及 Final 方法的模拟支持。而程序员时常都会发现自己有对以上这些方法的模拟需求,特别是当一个已有的软件系统摆在面前时。幸好 , 还有 PowerMock。

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



2、mock底层原理

①Mockito底层使用了动态代理,用到了CGLIB。因此需要被mock的对象,Mockito都会生成一个子类继承该类,这也就是为什么final类、private方法、static方法不可以被Mock的原因

②powermock的底层原理

我们首先看powermock的依赖

可以看出来,它有两个重要的依赖:javassist和objenesis。

javassist是一个修改java字节码的工具包,objenesis是一个绕过构造方法来实例化一个对象的工具包。由此看来,PowerMock的本质是通过修改字节码来实现对静态和final等方法的mock的

下面是PowerMock的简单实现原理:

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

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

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



3、项目中引用PowerMock

可以在POM文件中进行如下配置来在项目中引入PowerMock辅助我们进行单元测试:

<properties>
    <powermock.version>1.5.6</powermock.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <version>${powermock.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito</artifactId>
        <version>${powermock.version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>



4、重点API讲解


Mock


Powermockito.mock() 方 法 主 要 是 根 据 class 创 建 一 个 对 应 的 mock 对 象 ,

powermock 的创建方式可不像 easymock 等使用 proxy 的方式创建,他是会在你运行的

过程中动态的修改 class 字节码文件的形式来创建的。


spy


如果一个对象,只希望mock它的部分方法,而其他方法希望和真实对象的行为一致,可以使用spy。时,没有通过when设置过的方法,测试调用时,行为和真实对象一样


DoReturn…when…then


我们可以看到,每次当我们想给一个 mock 的对象进行某种行为的预期时,都会使用

do…when…then…这样的语法,其实理解起来非常简单:做什么、在什么时候、然后返回

什么。DoReturn不会进入mock方法的内部


when…thenReturn


其实理解起来非常简单:做什么、在什么时候、然后返回

什么。需要注意的是:mock的对象,所有没有调用when设置过的方法,在测试时调用,返回的都是对应返回类型的默认值。when…thenReturn会进入mock方法的内部


doNothing().when(…)…


调用后什么都不做的


doThrow(Throwable).when(…)…


调用后抛异常


Verify


当我们测试一个 void 方法的时候,根本没有办法去验证一个 mock 对象所执行后的结

果,因此唯一的方法就是检查方法是否被调用,在后文中将还会专门来讲解。


@RunWith


显式的告诉 Junit 使用某个指定的 Runner 来运行 Test Case,我

们使用了 PowerMockRunner 来运行我们的测试用例,如果不指定的话我们就默认使用的

是 Junit 提供的 Runner,先来看看下面这张图,了解一下 Runner 家族的成员

在这里插入图片描述


@PrepareForTest


PowerMock的Runner提前准备一个已经根据某种预期改变过的class,PowerMockito mock私有方法,静态方法和final方法的时候添加这个注解,可以作用在类和方法(某些情况下不起作用)上


通配符


可能存在这种情况:测试时对于传入的参数或者传出的参数并不关心,这时可以使用通配符:通过PowerMockito.anyInt()、PowerMockito.anyString、甚至PowerMockito.any(Class clazz)……来表示任意值,**需要注意的是,如果一个方法中,有一个地方使用了通配符,其他参数也都要使用通配符,对于特定的参数,不能直接指定,而需要使用PowerMockito.eq(someArg)来通配这个参数



版权声明:本文为menglei16原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。