PowerMock 框架也是属于 Mock 框架的一种,它是从 EasyMock 和 Mockito 两个框架扩展而来的,其最大的优点就是可以 mock 或者屏蔽一些私有、静态、final 等方法。
PowerMock 正如它的名字一样,是一种“强大”的 mock 框架。PowerMock 和 Mock 一样,主要为了解决代码中难以构建或者难以测试部分,它通过在测试用例运行期间修改字节码文件实现 mock、stub 或其它功能。它涵盖了 EasyMock
和Mockito
的所有功能,同时对这两个框架进行了扩展,弥补了这两个框架不能 mock 私有、静态、final 方法及变量等缺点。
PowerMock 框架支持 JUnit 以及 TestNG,同时支持多个版本的 JUnit 版本,分别为 JUnit 4.4+
和JUnit 4.0~4.3
,在 PowerMock 2.0 版本之后,JUnit 3 已被废除。
Android 配置 PowerMock
Android Studio 中使用 PowerMock 框架需要添加如下依赖:
dependencies { |
基本注释
-
@RunWith(PowerMockRunner.class)
如果测试用例使用 PowerMock 框架运行,则需要在测试类前添加
@RunWith(PowerMockRunner.class)
注释,PowerMockRunner
也可以替换成自定义的继承自PowerMockRunner
的 Runner; -
@PrepareForTest({SomeClass.class}, ...)
此注释意为告诉 PowerMock 准备一些测试所必须的类,包括一些 final 修饰的类,包含静态方法或变量、私有方法或变量以及 native 修饰的方法。
该注释可以用来注释某一(些)测试类,也可以修饰特定测试方法,这取决于你测试用例的设计,如果用来注释测试类,则 PowerMock 会为该测试类中的所有方法准备所指定的类,如果在测试类和测试方法都用
PrepareForTest()注释
,则测试类方法的注释会覆盖测试类的注释。 -
@PrepareOnlyThisForTest({SomeClass.class}, ...)
和
PrepareForTest()
方法类似,两者的区别在于PrepareOnlyThisForTest()
只针对某个特定的类,不对类的层次结构进行操作,但是PrepareForTest()
会对整个类的层次结构进行操作。 -
@PrepareEverythingForTest()
此注释意为准备除特定的系统和测试相关类以外的所有类用来测试,但是同样会加载静态初始化块。
-
@PoweMockIgnore
在正常情况下,PowerMock 会使用
MockClassLoader
加载所有的类,但是在某些情况下不需要使用 PowerMock 的类加载器加载,例如在 PowerMock 和 Robolectric 框架搭配使用时,此时需要使用@PowerMockIgnore
对其进行注释,其参数可以是某一个类也可以包,例如(PowerMockRunner.class)
"cc.istarx.MainActivity","android.*"}) ({
public class ExamplePowerMockTest {
}但是使用
@PowerMockIgnore
注释的最大的缺点就是必须对每个不同的测试类都需要该注释,这样就造成了相同的代码在很多处都存在,给维护带来很大的不便。对于这种情况有两种方法可以解决:-
编写基类,并把公有的类和包在基类中使用
@PowerMockIgnore
注释,并让所有的测试类都继承该基类(PowerMockRunner.class)
"cc.istarx.MainActivity","android.*"}) ({
public class BaseClass {
// 基类
}
public TestClass1 extends BaseClass {
public void test1(){
// do some test here
}
...
} -
利用 PowerMock 1.7.0 版本之后可以使用配置文件指定忽略加载的类和包,例如:
powermock.global-ignore="cc.istarx.MainActivity" // 单个类
powermock.global-ignore="cc.istarx.MainActivity","android.*" // 多个类和包所有用户定义的配置文件在目录
org/powermock/extensions/configuration.properties
中。如果在某一测试类中需要取消这些配置,则只需将globalIgnore
值置为false
即可。例如:(PowerMockRunner.class)
false) (globalIgnore =
public TestClass {
...
}
当
@PrepareForTest
、@PrepareOnlyThisForTest
、@PrepareEverythingForTest
、@PowerMockIgnore
注释同时出现时,@PrepareForTest
、@PrepareOnlyThisForTest
优先级要高于@PowerMockIgnore
,但是@PrepareEverythingForTest
的优先级要低于@PowerMockIgnore
。 -
操作私有属性和方法
正常情况下,为降低代码的侵入性,不介意修改私有变量或者操作私有方法和构造函数。但是为了达到更高的覆盖率,防止后面代码重构破坏原有功能,这种情况下操作私有变量、方法以及构造函数是很有必要的。
PowerMock 提供了一个用来操作私有属性、方法以及构建函数,该类中提供了操作私有变量、方法、构造函数的方法。例如:
Whitebox.newInstance(Class<T> classToInstantiate); // 不执行构造函数而实例化 |
一个完整的例子如下:
被测试类:
public class WhiteboxDemo { |
对应的测试用例:
|
屏蔽执行
在被测代码中,某些方法的执行对测试带来了阻碍,比如 System.loadLibrary()
等。类似的系统方法可以利用 PowerMock 框架可以模拟出来,但是某些情况下,比如某个变量、构造函数等,不能利用模拟的方法解决这类问题,PowerMock 提供了一种方法可以绕过这些方法、变量、构造函数的执行,极大的提高了测试的便利性。
PowerMock 用来绕过执行的方法是suppress()
,该方法在org.powermock.api.support.membermodification.MemberModifier
类中,该方法的参数可以是 Mehtod
、Fied
、constructor
,如果同时绕过多个Field
或者constructor
,则可以构建一维数组进行传递。
绕过某个方法、变量、构造函数需要注意一下几点:
- 测试用例所在的测试类必须要以
@RunWith(PowerMockRunner.class)
进行注释; - 如果要屏蔽某个类的静态初始化块,则需要对对测试类添加注释
@SuppressStaticInitializationFor(class.want.to.suppress.ClassName)
; - 如果要对某个类的构造函数、方法、变量进行屏蔽,则需要在
测试类
之前添加@PrepareForTest()
注释;
例如有如下代码:
public class SuppressDemo { |
对应的测试用例如下:
(PowerMockRunner.class) |
Mock 静态方法
在实际项目测试中,被测代码中难免会有静态方法,给测试带来了一定的难度。PowerMock 比 Mockito 框架好的地方就是可以对静态、私有、final类型的方法等进行模拟。PoweMock 在模拟一个静态方法时有一套固定的流程,主要步骤如下:
-
以
@RunWith(PowerMockRunner.class)
对测试类进行注释; -
对包含静态方法的类以
@PrepareForTest()
注释,所准备的类为包含静态方法的类,次注释可以是类级别也可以是测试方法级别; -
在测试用例中的步骤如下:
PowerMock.mockStatic(WithStaticMethod.class);
when(WithStaticMethod.getNum()).thenReturn(11);
// 执行静态方法
PowerMockito.verifyStatic(WithStaticMethod.class);
WithStaticMethod.getNum(); -
在
verify
的时候同样可以进行参数匹配或者次数验证:PowerMockito.verifyStatic(WithStaticMethod.class, times(1));
WithStaticMethod.getNumWithNum(anyInt());必须在每个静态方法的验证之前调用
PowerMockito.verifyStatic(Static.class)
举例:
// 被测试代码 |
Mock 私有方法
PowerMock 提供了验证私有方法的方法verifyPrivate(tested).invoke("privateMethodName", argument1)
,例如:
待测试代码:
public class VerifyPrivateAndConstructor { |
测试用例:
|
Mock 构造函数
Mock 构造函数时必须用@PrepareForTest(XXX.class)
对测试方法或者类进行注释,并用PowerMock.whenNew()
对构造函数进行 mock,例如:
(expected = IllegalArgumentException.class) |
总结
PowerMock 正如其名,确实很强大,可以解决一些测试中很难测试的点或者和框架、系统进行交互的地方,给测试带来了极大的便利性。PowerMock 也可以和其它框架进行配合使用,比如 Robolectric,关于 Robolectric 后续会有专门的介绍。
谢谢阅读,希望能给你带来帮助。
本文示例代码:PowerMock Demo