PowerMockito框架使用
Mockito框架基本满足需求但是有一些局限性,如对static、final、private等方法不能mock,PowerMockito就可以解决这些问题,PowerMockito是一个扩展了其它如EasyMock等mock框架的、功能更加强大的框架。PowerMock使用一个自定义类加载器和字节码操作来模拟静态方法,构造函数,final类和方法,私有方法,去除静态初始化器等等。
添加依赖:
testImplementation 'org.powermock:powermock-module-junit4:2.0.2' testImplementation 'org.powermock:powermock-module-junit4-rule:2.0.2' testImplementation 'org.powermock:powermock-api-mockito2:2.0.2' testImplementation 'org.powermock:powermock-classloading-xstream:2.0.2' |
1. 普通Mock的方式
目标类:
public class CommonExample { public boolean callArgumentInstance(File file) { return file.exists(); } } |
测试类:
public class CommonExamplePowerMockTest { @Test public void testCallArgumentInstance() { File file = PowerMockito.mock(File.class); CommonExample commonExample = new CommonExample(); PowerMockito.when(file.exists()).thenReturn(true); Assert.assertTrue(commonExample.callArgumentInstance(file)); } } |
普通Mock方式是外部传递Mock参数,基本上和单独使用Mockito是一样的,使用纯Mockito的api也可以完成这个测试。
2. Mock方法内部new出来的对象
public class CommonExample { public boolean callArgumentInstance(String path) { File file = new File(path); return file.exists(); } } |
@RunWith(PowerMockRunner.class) @PrepareForTest(CommonExample.class) public class CommonExamplePowerMockTest { @Test public void callCallArgumentInstance2() throws Exception { File file = PowerMockito.mock(File.class); CommonExample commonExample = new CommonExample(); PowerMockito.whenNew(File.class).withArguments("aaa").thenReturn(file); PowerMockito.when(file.exists()).thenReturn(true); Assert.assertTrue(commonExample.callArgumentInstance("aaa")); } } |
跟前面有一点区别的就是,这里要测试的方法内部创建了File对象,这时需要通过PowerMockito.whenNew(File.class).withArguments("aaa").thenReturn(file)方法模拟创建File的操作,当File类以aaa的参数创建的时候返回已经mock出来的file对象。同时这时需要在测试类上添加注解@RunWith(PowerMockRunner.class)和@PrepareForTest(CommonExample.class),注意是在类上面添加,不是在方法上,一开始在方法上添加时提示找不到测试方法,@PrepareForTest()括号里面指定的是要测试的目标类。
3. Mock普通对象的final方法
public class CommonExample { public boolean callFinalMethod(DependencyClass dependency) { return dependency.isValidate(); } } public class DependencyClass { public final boolean isValidate() { // do something return false; } } |
@RunWith(PowerMockRunner.class) @PrepareForTest({CommonExample.class, DependencyClass.class}) public class CommonExamplePowerMockTest { @Test public void callFinalMethod() { DependencyClass dependency = PowerMockito.mock(DependencyClass.class); CommonExample commonExample = new CommonExample(); PowerMockito.when(dependency.isValidate()).thenReturn(true); Assert.assertTrue(commonExample.callFinalMethod(dependency)); } } |
同样这里mock出来需要依赖的类的对象,然后传递给调用方法,这里同样需要添加@RunWith和@PrepareForTest,@PrepareForTest可以指定多个目标类,但是这里如果你只需要测试final的话,只添加DependencyClass.class一个就可以了。
4. Mock普通类的静态方法
public final class Utils { public static String getUUId() { return UUID.randomUUID().toString(); } } public class CommonExample { public String printUUID() { return Utils.getUUId(); } } |
@RunWith(PowerMockRunner.class) @PrepareForTest(Utils.class) public class StaticUnitTest { @Before public void setUp() throws Exception { PowerMockito.mockStatic(Utils.class); } @Test public void getUUId() { PowerMockito.when(Utils.getUUId()).thenReturn("FAKE UUID"); CommonExample commonExample = new CommonExample(); assertThat(commonExample.printUUID(), is("FAKE UUID")); } } |
同样需要指定@RunWith和@PrepareForTest,@PrepareForTest中指定静态方法所在的类,测试静态方法之前需要调用PowerMockito.mockStatic()方法来mock静态类,然后就通过when().thenReturn()方法指定静态方法的模拟返回值即可。
5. verify静态方法的调用次数
@Test public void testVerify() { PowerMockito.when(Utils.getUUId()).thenReturn("FAKE UUID"); CommonExample commonExample = new CommonExample(); System.out.println(commonExample.printUUID()); PowerMockito.verifyStatic(Utils.class); Utils.getUUId(); } |
静态方法通过PowerMockito.verifyStatic(Class c)进行验证,不过这里跟Mocktio有一点区别的是需要在这个方法的后面再调用一次静态方法,否则不行。这里PowerMockito.verifyStatic(Utils.class)其实等同于PowerMockito.verifyStatic(Utils.class, times(1)),如果想验证超过一次的,那么:
@Test public void testVerify() { PowerMockito.when(Utils.getUUId()).thenReturn("FAKE UUID"); CommonExample commonExample = new CommonExample(); System.out.println(commonExample.printUUID()); System.out.println(commonExample.printUUID()); PowerMockito.verifyStatic(Utils.class, Mockito.times(2)); Utils.getUUId(); } |
这时PowerMockito.verifyStatic()第一个参数指定静态方法类的Class,第二个参数接收一个VerificationMode类型的参数,因此传递Mockito中的任何验证方法次数的函数都可以,Mockito中的验证函数会返回的是一个VerificationMode类型。同样在PowerMockito.verifyStatic方法后面要调用一次要验证的静态方法,总感觉这里很奇怪。。。
6. 使用真实返回值
如果在测试的过程中又遇到不需要mock出来的静态方法的模拟返回值,而是需要真实的返回值,怎么办呢,其实跟Mockito一样,PowerMockito同样提供thenCallRealMethod或者doCallRealMethod方法:
@Test public void testRealCall() throws Exception { PowerMockito.when(Utils.getUUId()).thenReturn("FAKE UUID"); //... PowerMockito.when(Utils.getUUId()).thenCallRealMethod(); //与下面等价 //PowerMockito.doCallRealMethod().when(Utils.class, "getUUId"); System.out.println(Utils.getUUId()); } |
或者直接使用spy监控真实对象也可以:
@Test public void testRealCall() { PowerMockito.spy(Utils.class); System.out.println(Utils.getUUId()); } |
7. Mock私有方法
public class CommonExample { public boolean callPrivateMethod() { return isExist(); } private boolean isExist() { return false; } } |
@RunWith(PowerMockRunner.class) @PrepareForTest(CommonExample.class) public class PrivateUnitTest { @Test public void testCallPrivateMethod() throws Exception { CommonExample commonExample = PowerMockito.mock(CommonExample.class); PowerMockito.when(commonExample.callPrivateMethod()).thenCallRealMethod(); PowerMockito.when(commonExample, "isExist").thenReturn(true); Assert.assertTrue(commonExample.callPrivateMethod()); } } |
在使用上跟纯Mockito的没有太大区别,只不过Mock私有方法是通过下面的api实现的:
PowerMockito.when(Object instance, String methodName, Object... arguments) |
在PowerMockito中when函数与Mockito相比,最大的变化就是多了一些传递String类型的methodName的重载方法,这样在使用上几乎无所不能了。
8. Mock普通类的私有变量
public class CommonExample { private static final int STATE_NOT_READY = 0; private static final int STATE_READY = 1; private int mState = STATE_NOT_READY; public boolean doSomethingIfStateReady() { if (mState == STATE_READY) { // DO some thing return true; } else { return false; } } } |
@Test public void testDoSomethingIfStateReady() throws Exception { CommonExample sample = new CommonExample(); Whitebox.setInternalState(sample, "mState", 1); assertThat(sample.doSomethingIfStateReady(), is(true)); } |
通过Whitebox.setInternalState来改变私有成员变量,这种情况下不需要指定@RunWith和@PrepareForTest。
9. 对静态void方法进行Mock
public class CommonExample { public static void doSomething(String a) { System.out.println("doSomething"+a); } } |
@RunWith(PowerMockRunner.class) @PrepareForTest({CommonExample.class}) public class StaticUnitTest { @Test public void testStaticVoid() throws Exception { PowerMockito.mockStatic(CommonExample.class); PowerMockito.doNothing().when(CommonExample.class, "doSomething", Mockito.any()); CommonExample.doSomething("aaa"); } } |
默认情况下通过PowerMockito.mockStatic的静态类的void的方法是什么也不做的,但是可以显示的执行doNothing, 上面的代码将doNothing那行注释掉也是什么也不做的。那如果想做一些事而不是doNothing呢,跟Mockito一样,采用doAnswer:
@Test public void testStaticVoid() throws Exception { PowerMockito.mockStatic(CommonExample.class); PowerMockito.doAnswer(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { System.out.println(invocation.getArguments()[0]); return null; } }).when(CommonExample.class, "doSomething", Mockito.any()); CommonExample.doSomething("aaa"); } |
10. Mock系统的final静态类
public class CommonExample { public int callSystemStaticMethod(int a, int b) { return Math.max(a, a+b); } } |
@RunWith(PowerMockRunner.class) @PrepareForTest(CommonExample.class) public class StaticUnitTest { @Test public void callSystemStaticMethod() { CommonExample commonExample = new CommonExample(); PowerMockito.mockStatic(Math.class); PowerMockito.when(Math.max(anyInt(), anyInt())).thenReturn(100); Assert.assertEquals(100, commonExample.callSystemStaticMethod(10, -5)); } } |
@PrepareForTest中添加调用系统类所在的类,这里需要注意的是如果你使用PowerMockito来mock系统静态final类,则gradle依赖中不能再添加单纯Mockito的依赖库,否则这里将不能mock成功,会提示Mockito can not mock/spy final class, 因为PowerMockito本身已经有对Mockito的依赖库支持了,所以只依赖PowerMockito就可以了。除了系统静态final类的情况,其他的情况下PowerMockito和Mockito可以同时依赖(我测试是没有问题的)。另外单纯的Mockito新版本中也支持对 final 类 final 方法的 Mock,但是需要添加配置文件并不友好。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理