单元测试(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 是一个真实的对象,但它可以设置对象行为