Android单元测试之PowerMockito框架使用

发表于:2021-1-12 09:36

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:chenxibobo    来源:博客园

  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),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号