使用 PowerMock 进行单元测试
单元测试(Unit Testing)又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。如果我们写的代码依赖于某些模块对象,而单元测试过程中这些对象又很难手动创建,或者模块还没有开发完成,那么就使用一个虚拟的对象来完成单元测试,这就是所谓的 Mock。
Java 单元测试中比较流行的 Mock 测试框架有
jMock
、
EasyMock
、
Mockito
,但是这些 Mock 工具都不能 Mock static
、final
、private
方法等,而
PowerMock
能够做到。
使用 PowerMock,首先需要使用 @RunWith(PowerMockRunner.class)
将测试用例的 Runner 改为 PowerMockRunner
。如果要 Mock static
、final
、private
等方法的时候,就需要加注解 @PrepareForTest
。
PowerMock 有两个版本,一个是基于 EasyMock 实现的,另一个是基于 Mockito 实现的。
下面我将以 PowerMock 的 Mockito 的版本来讲述如何使用 PowerMock。
1. 普通 Mock(Mock 参数传递的对象)
测试对象
public class ClassUnderTest {
public boolean callArgumentInstance(File file) {
return file.exists();
}
}
测试用例
public class TestClassUnderTest {
@Test
public void testCallArgumentInstance() {
// Mock 对象,也可以使用 org.mockito.Mock 注解标记来实现
File file = PowerMockito.mock(File.class);
ClassUnderTest underTest = new ClassUnderTest();
// 录制 Mock 对象行为
PowerMockito.when(file.exists()).thenReturn(true);
// 验证方法行为
Assert.assertTrue(underTest.callArgumentInstance(file));
}
}
普通 Mock 不需要加 @RunWith
和 @PrepareForTest
注解。
2. Mock 方法内部 new 出来的对象
测试对象
public class ClassUnderTest {
public boolean callInternalInstance(String path) {
File file = new File(path);
return file.exists();
}
}
测试用例
// 必须加注解 @PrepareForTest 和 @RunWith
@RunWith(PowerMockRunner.class)
public class TestClassUnderTest {
@Test
// 在测试方法之上需要添加注解 @PrepareForTest,注解里写的类是需要 Mock 的 new 对象代码所在的类。
@PrepareForTest(ClassUnderTest.class)
public void testCallInternalInstance() throws Exception {
File file = PowerMockito.mock(File.class);
ClassUnderTest underTest = new ClassUnderTest();
// 当以参数为 bbb 创建 File 对象的时候,返回已经 Mock 的 File 对象。
PowerMockito.whenNew(File.class).withArguments("bbb").thenReturn(file);
PowerMockito.when(file.exists()).thenReturn(true);
Assert.assertTrue(underTest.callInternalInstance("bbb"));
}
}
3. Mock 普通对象的 final 方法
测试对象
public class ClassUnderTest {
public boolean callFinalMethod(ClassDependency refer) {
return refer.isAlive();
}
}
public class ClassDependency {
public final boolean isAlive() {
// do something
return false;
}
}
测试用例
// 必须加注解 @PrepareForTest 和 @RunWith
@RunWith(PowerMockRunner.class)
public class TestClassUnderTest {
@Test
// 在测试方法之上加注解 @PrepareForTest,注解里写的类是需要 Mock 的 final 方法所在的类。
@PrepareForTest(ClassDependency.class)
public void testCallFinalMethod() {
ClassDependency depencency = PowerMockito.mock(ClassDependency.class);
ClassUnderTest underTest = new ClassUnderTest();
PowerMockito.when(depencency.isAlive()).thenReturn(true);
Assert.assertTrue(underTest.callFinalMethod(depencency));
}
}
4. Mock 静态方法。
测试对象
public class ClassUnderTest {
public boolean callStaticMethod() {
return ClassDependency.isExist();
}
}
public class ClassDependency {
public static boolean isExist() {
// do something
return false;
}
}
测试用例
// 必须加注解 @PrepareForTest 和 @RunWith
@RunWith(PowerMockRunner.class)
public class TestClassUnderTest {
@Test
// 在测试方法之上加注解 @PrepareForTest,注解里写的类是需要 Mock 的 static 方法所在的类。
@PrepareForTest(ClassDependency.class)
public void testCallStaticMethod() {
ClassUnderTest underTest = new ClassUnderTest();
// 表示需要 Mock 这个类里的静态方法
PowerMockito.mockStatic(ClassDependency.class);
PowerMockito.when(ClassDependency.isExist()).thenReturn(true);
Assert.assertTrue(underTest.callStaticMethod());
}
}
5. Mock 私有方法
测试对象
public class ClassUnderTest {
public boolean callPrivateMethod() {
return isExist();
}
private boolean isExist() {
return false;
}
}
测试用例
// 必须加注解 @PrepareForTest 和 @RunWith
@RunWith(PowerMockRunner.class)
public class TestClassUnderTest {
@Test
// 在测试方法之上加注解 @PrepareForTest,注解里写的类是需要 Mock 的 private 方法所在的类。
@PrepareForTest(ClassUnderTest.class)
public void testCallPrivateMethod() throws Exception {
ClassUnderTest underTest = PowerMockito.mock(ClassUnderTest.class);
PowerMockito.when(underTest.callPrivateMethod()).thenCallRealMethod();
PowerMockito.when(underTest, "isExist").thenReturn(true);
Assert.assertTrue(underTest.callPrivateMethod());
}
}
6. Mock JDK 中类的静态、私有方法。
测试对象
public class ClassUnderTest {
public boolean callSystemFinalMethod(String str) {
return str.isEmpty();
}
public String callSystemStaticMethod(String str) {
return System.getProperty(str);
}
}
测试用例
// 必须加注解 @PrepareForTest 和 @RunWith
@RunWith(PowerMockRunner.class)
public class TestClassUnderTest
@Test
// 和 Mock 普通对象的 static、final 方法一样,只不过注解 @PrepareForTest 里写的类不一样
// 注解里写的类是需要调用系统方法所在的类。
@PrepareForTest(ClassUnderTest.class)
public void testCallSystemFinalMethod() {
String str = PowerMockito.mock(String.class);
ClassUnderTest underTest = new ClassUnderTest();
PowerMockito.when(str.isEmpty()).thenReturn(false);
Assert.assertFalse(underTest.callSystemFinalMethod(str));
}
@Test
@PrepareForTest(ClassUnderTest.class)
public void testCallSystemStaticMethod() {
ClassUnderTest underTest = new ClassUnderTest();
PowerMockito.mockStatic(System.class);
PowerMockito.when(System.getProperty("aaa")).thenReturn("bbb");
Assert.assertEquals("bbb", underTest.callSystemStaticMethod("aaa"));
}
}
7. Mock 依赖类中的方法(whenNew)
测试对象
public class ClassUnderTest {
public boolean callDependency() {
ClassDependency classDependency = new ClassDependency();
return classDependency.isGod("hh");
}
}
public class ClassDependency {
public boolean isGod(String oh){
System.out.println(oh);
return false;
}
}
测试用例
// 必须加注解 @PrepareForTest 和 @RunWith
@RunWith(PowerMockRunner.class)
public class TestClassUnderTest {
@Test
// 注解里写的类是依赖类所在的类。
@PrepareForTest(ClassUnderTest.class)
public void testDependency() throws Exception {
ClassUnderTest underTest = new ClassUnderTest();
ClassDependency dependency = mock(ClassDependency.class);
whenNew(ClassDependency.class).withAnyArguments().thenReturn(dependency);
when(dependency.isGod(anyString())).thenReturn(true);
Assert.assertTrue(underTest.callDependency());
}
}
8. 完整示例代码
测试目标类
package cn.enncloud.ceres.powermock;
import java.io.File;
/**
* Created by lixiangrong on 2017/7/21.
*/
public class ClassUnderTest {
public boolean callArgumentInstance(File file) {
return file.exists();
}
public boolean callInternalInstance(String path) {
File file = new File(path);
return file.exists();
}
public boolean callFinalMethod(ClassDependency refer) {
return refer.isAlive();
}
public boolean callSystemFinalMethod(String str) {
return str.isEmpty();
}
public boolean callStaticMethod() {
return ClassDependency.isExist();
}
public String callSystemStaticMethod(String str) {
return System.getProperty(str);
}
public boolean callPrivateMethod() {
return isExist();
}
public boolean callVoidPrivateMethod(){
testVoid();
return true;
}
private boolean isExist() {
// do something
return false;
}
private void testVoid(){
System.out.println("do nothing");
}
public boolean callDependency() {
ClassDependency classDependency = new ClassDependency();
return classDependency.isGod("hh");
}
}
依赖类
package cn.enncloud.ceres.powermock;
/**
* Created by lixiangrong on 2017/7/21.
*/
public class ClassDependency {
public static boolean isExist() {
// do something
return false;
}
public final boolean isAlive() {
// do something
return false;
}
public boolean isGod(String oh){
System.out.println(oh);
return false;
}
}
测试用例
package cn.enncloud.ceres.powermock.test;
import cn.enncloud.ceres.powermock.ClassDependency;
import cn.enncloud.ceres.powermock.ClassUnderTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.io.File;
/**
* Created by lixiangrong on 2017/7/21.
*/
@RunWith(PowerMockRunner.class)
public class TestClassUnderTest {
@Test
public void 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 {
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() {
ClassDependency depencency = PowerMockito.mock(ClassDependency.class);
ClassUnderTest underTest = new ClassUnderTest();
PowerMockito.when(depencency.isAlive()).thenReturn(true);
Assert.assertTrue(underTest.callFinalMethod(depencency));
}
@Test
@PrepareForTest(ClassUnderTest.class)
public void testCallSystemFinalMethod() {
String str = PowerMockito.mock(String.class);
ClassUnderTest underTest = new ClassUnderTest();
PowerMockito.when(str.isEmpty()).thenReturn(false);
Assert.assertFalse(underTest.callSystemFinalMethod(str));
}
@Test
@PrepareForTest(ClassDependency.class)
public void testCallStaticMethod() {
ClassUnderTest underTest = new ClassUnderTest();
PowerMockito.mockStatic(ClassDependency.class);
PowerMockito.when(ClassDependency.isExist()).thenReturn(true);
Assert.assertTrue(underTest.callStaticMethod());
}
@Test
@PrepareForTest(ClassUnderTest.class)
public void testCallSystemStaticMethod() {
ClassUnderTest underTest = new ClassUnderTest();
PowerMockito.mockStatic(System.class);
PowerMockito.when(System.getProperty("aaa")).thenReturn("bbb");
Assert.assertEquals("bbb", underTest.callSystemStaticMethod("aaa"));
}
@Test
@PrepareForTest(ClassUnderTest.class)
public void testCallPrivateMethod() throws Exception {
ClassUnderTest underTest = PowerMockito.mock(ClassUnderTest.class);
PowerMockito.when(underTest.callPrivateMethod()).thenCallRealMethod();
PowerMockito.when(underTest, "isExist").thenReturn(true);
Assert.assertTrue(underTest.callPrivateMethod());
}
@Test
@PrepareForTest(ClassUnderTest.class)
public void testCallVoidPrivateMethod() throws Exception {
ClassUnderTest underTest = PowerMockito.mock(ClassUnderTest.class);
PowerMockito.when(underTest.callVoidPrivateMethod()).thenCallRealMethod();
PowerMockito.doNothing().when(underTest, "testVoid");
Assert.assertTrue(underTest.callVoidPrivateMethod());
}
@Test
@PrepareForTest(ClassUnderTest.class)
public void testDependency() throws Exception {
ClassUnderTest underTest = new ClassUnderTest();
ClassDependency dependency = mock(ClassDependency.class);
// @PrepareForTest(ClassUnderTest.class)
whenNew(ClassDependency.class).withAnyArguments().thenReturn(dependency);
when(dependency.isGod(anyString())).thenReturn(true);
Assert.assertTrue(underTest.callDependency());
}
}
9. Mock 与 Spy
Mock 不是真实的对象,它只是用类型的 class 创建了一个虚拟对象,并可以设置对象行为 Spy 是一个真实的对象,但它可以设置对象行为