单元测试(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 参数传递的对象)
测试对象
1 2 3 4 5
| public class ClassUnderTest { public boolean callArgumentInstance(File file) { return file.exists(); } }
|
测试用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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)); } }
|
普通 Mock 不需要加 @RunWith
和 @PrepareForTest
注解。
2. Mock 方法内部 new 出来的对象
测试对象
1 2 3 4 5 6
| public class ClassUnderTest { public boolean callInternalInstance(String path) { File file = new File(path); return file.exists(); } }
|
测试用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @RunWith(PowerMockRunner.class) public class TestClassUnderTest { @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")); } }
|
3. Mock 普通对象的 final 方法
测试对象
1 2 3 4 5
| public class ClassUnderTest { public boolean callFinalMethod(ClassDependency refer) { return refer.isAlive(); } }
|
1 2 3 4 5 6
| public class ClassDependency { public final boolean isAlive() { return false; } }
|
测试用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @RunWith(PowerMockRunner.class) public class TestClassUnderTest { @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)); } }
|
4. Mock 静态方法。
测试对象
1 2 3 4 5
| public class ClassUnderTest { public boolean callStaticMethod() { return ClassDependency.isExist(); } }
|
1 2 3 4 5 6
| public class ClassDependency { public static boolean isExist() { return false; } }
|
测试用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @RunWith(PowerMockRunner.class) public class TestClassUnderTest { @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()); } }
|
5. Mock 私有方法
测试对象
1 2 3 4 5 6 7 8 9 10
| public class ClassUnderTest {
public boolean callPrivateMethod() { return isExist(); }
private boolean isExist() { return false; } }
|
测试用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @RunWith(PowerMockRunner.class) public class TestClassUnderTest { @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()); } }
|
6. Mock JDK 中类的静态、私有方法。
测试对象
1 2 3 4 5 6 7 8 9 10
| public class ClassUnderTest {
public boolean callSystemFinalMethod(String str) { return str.isEmpty(); }
public String callSystemStaticMethod(String str) { return System.getProperty(str); } }
|
测试用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| @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)
测试对象
1 2 3 4 5 6 7
| public class ClassUnderTest {
public boolean callDependency() { ClassDependency classDependency = new ClassDependency(); return classDependency.isGod("hh"); } }
|
1 2 3 4 5 6
| public class ClassDependency { public boolean isGod(String oh){ System.out.println(oh); return false; } }
|
测试用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @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. 完整示例代码
测试目标类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package cn.enncloud.ceres.powermock;
import java.io.File;
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() { return false; }
private void testVoid(){ System.out.println("do nothing"); }
public boolean callDependency() { ClassDependency classDependency = new ClassDependency(); return classDependency.isGod("hh"); } }
|
依赖类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package cn.enncloud.ceres.powermock;
public class ClassDependency {
public static boolean isExist() { return false; }
public final boolean isAlive() { return false; }
public boolean isGod(String oh){ System.out.println(oh); return false; } }
|
测试用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
| 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;
@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);
whenNew(ClassDependency.class).withAnyArguments().thenReturn(dependency);
when(dependency.isGod(anyString())).thenReturn(true); Assert.assertTrue(underTest.callDependency()); } }
|
9. Mock 与 Spy
Mock 不是真实的对象,它只是用类型的 class 创建了一个虚拟对象,并可以设置对象行为
Spy 是一个真实的对象,但它可以设置对象行为