单元测试之Powermock用法的介绍

发表于:2024-3-06 09:27

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

 作者:大师傅姑爷    来源:稀土掘金

  简介
  Mockito的工作原理是通过创建依赖对象的proxy,所有的调用先经过proxy对象,proxy对象拦截了所有的请求再根据预设的返回值进行处理。Mockito虽然功能强大,但它不能mock静态方法、构造方法、私有方法以及 Final 方法。而很多时候这些方法也是需要测试的,因此PowerMock诞生了。PowerMock在Mockito原有的基础上做了扩展,通过修改类字节码并使用自定义ClassLoader加载运行的方式来实现mock静态方法、构造方法、final方法、private方法、系统类的功能。
  在使用上,PowerMock与Mockito略有不同,首先是测试类上的@RunWith注解需要修改为:
  @RunWith(PowerMockRunner.class)
  第二是需要使用到@PrepareForTest注解(PrepareFotTest注解会修改传入参数类的字节码,通过修改字节码达到模拟final、static、私有方法、系统类等的功能),此注解可写在类上也可写在方法上:
  @PrepareForTest({Apple.class, Singleton.class})
  在使用PowerMock时,@RunWith(PowerMockRunner.class)和@PrepareForTest({**.class})必须要有,否则会报错:org.powermock.api.mockito.ClassNotPreparedException。
  常用方法
  首先我们定义一个Laptop类,Apple继承于它。其中有我们后面需要mock的static、private等方法。
  public class Laptop {
     private String cpu = "intel";
   
     public String getCpu(){
         System.out.println("getCpu");
         return cpu;
    }
  }
   
   
  public class Apple extends Laptop{
     private static String TAG = Apple.class.getName();
   
     public Apple() {
    }
   
     public static String getTAG(){
         return TAG;
    }
   
     public static String getColor() {
         System.out.println("getColor");
         return "深灰色";
    }
   
     public String getAppleInfo() {
         System.out.println("getAppleInfo");
         return getFlavor() + getColor();
    }
   
     private String getFlavor() {
         System.out.println("getFlavor");
         return "快得很";
    }
   
     public final int getStorage() {
         System.out.println("getStorage");
         return 256;
    }
  }
  mock静态方法
  使用PowerMockito.mockStatic来mock静态方法:
     @Test
  @PrepareForTest({Apple.class})
     public void testMockStatic() throws Exception {
         PowerMockito.mockStatic(Apple.class);
         PowerMockito.when(Apple.getColor()).thenReturn("红色");
         String color = Apple.getColor();
         Assert.assertEquals("红色", color);
    }
  除此之外,还可以修改静态变量,不过使用的是:
     @Test
  @PrepareForTest({Apple.class})
     public void testChangeStatic() {
         Whitebox.setInternalState(Apple.class, "TAG", "Mock");//指定静态变量“TAG”的值为“mock”
         Assert.assertEquals("Apple", Apple.getTAG());//测试不通过
    }
  在实际开发中,我们还可以mock系统方法:
     @Test
  @PrepareForTest({Apple.class})
     public void testMockSystem() {
         PowerMockito.mockStatic(System.class);
         PowerMockito.when(System.getProperty("123")).thenReturn("abc");
         Assert.assertEquals("abc", System.getProperty("123"));
    }
  Mock私有方法
     @Test
  @PrepareForTest({Apple.class})
     public void testMockPrivate() throws Exception {
         Apple apple = PowerMockito.mock(Apple.class);
         //调用实际方法,否则不会调用getFlavor()
         PowerMockito.when(apple.getAppleInfo()).thenCallRealMethod();
         //mock private方法
         PowerMockito.when(apple, "getFlavor").thenReturn("快得很");
         System.out.println(apple.getAppleInfo());
         //验证getFlavor是否调用了一次
         PowerMockito.verifyPrivate(apple, Mockito.times(1)).invoke("getFlavor");
    }
  在开发中,如果遇到不想让私有方法执行的情况,比如私有方法太耗时等,我们可以不执行私有方法:
     @Test
     @PrepareForTest({Apple.class})
     public void testSkipPrivate() throws Exception {
         Apple apple = new Apple();
         PowerMockito.suppress(PowerMockito.method(Apple.class, "getFlavor"));
         System.out.println(apple.getAppleInfo());
    }
  除此之外,PowerMock还提供了修改父类私有变量的方法:PowerMockito.field(declaringClass, fieldName).set(instance, value),或者使用MemberModifier类也可以,具体参考下面的代码:
     @Test
     @PrepareForTest({Apple.class})
     public void testChangeFatherPrivate() throws Exception {
         Apple apple = new Apple();
         //更改父类私有变量
         PowerMockito.field(Apple.class, "cpu").set(apple, "m1");
  //       MemberModifier.field(Apple.class, "cpu").set(apple, "m1");
         Assert.assertEquals("m1", apple.getCpu());
    }
   
     @Test
     @PrepareForTest({Apple.class})
     public void testChangeFatherPrivate2() throws Exception {
         Apple apple = PowerMockito.mock(Apple.class);
         PowerMockito.when(apple.getCpu()).thenCallRealMethod();
         //更改父类私有变量
         PowerMockito.field(Apple.class, "cpu").set(apple, "m1");
  //       MemberModifier.field(Apple.class, "cpu").set(apple, "m1");
         Assert.assertEquals("m1", apple.getCpu());
    }
  mock final方法
     @Test
     @PrepareForTest({Apple.class})
     public void testMockFinal() {
         Apple apple = PowerMockito.mock(Apple.class);
         PowerMockito.when(apple.getStorage()).thenCallRealMethod();
         PowerMockito.when(apple.getStorage()).thenReturn(128);
         Assert.assertEquals(128, apple.getStorage());
    }
  mock构造方法
     @Test
     @PrepareForTest({Apple.class})
     public void testMockConstructor() throws Exception {
         Apple apple = PowerMockito.mock(Apple.class);
         PowerMockito.when(apple.getStorage()).thenReturn(512);
         PowerMockito.whenNew(Apple.class).withNoArguments().thenReturn(apple);
   
         Apple apple1 = new Apple();
         Assert.assertEquals(256, apple1.getStorage());
   
    }
  whenNew表示之后创建这个类的对象时,不再重新创建,而是直接返回某个被mock的对象。如果是有参数的构造方法,可以使用.withArguments()将参数传入,或者使用.withAnyArguments()。
  mock单例类
  首先定义一个单例类Singleton:
  public class Singleton {
     private static Singleton mInstance;
   
     public static Singleton getInstance() {
         if (mInstance == null) {
             mInstance = new Singleton();
        }
         return mInstance;
    }
   
     private Singleton() {
    }
   
     private String serialNum;
   
     public String getSerialNum() {
         return serialNum;
    }
  }
  测试用例
     @Test
     @PrepareForTest({Singleton.class})
     public void testMockSingleton() {
   
         PowerMockito.mockStatic(Singleton.class);
         Singleton mock = PowerMockito.mock(Singleton.class);
         PowerMockito.when(Singleton.getInstance()).thenReturn(mock);
         PowerMockito.when(mock.getSerialNum()).thenReturn("111");
         Assert.assertEquals("111", mock.getSerialNum());
   
         Singleton singleton = Singleton.getInstance();
         Assert.assertEquals("111", singleton.getSerialNum());
   
         Assert.assertEquals(mock.hashCode(), singleton.hashCode());
    }
  至此,有关Powermock的用法介绍完毕。
  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

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

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

沪ICP备05003035号

沪公网安备 31010102002173号