单元测试系列之五Powermock

1,721 阅读3分钟

单元测试系列之一开篇

单元测试系列之二如何运行单元测试

单元测试系列之三Junit

单元测试系列之四Mockito

单元测试系列之五Powermock

单元测试系列之六Espresso

单元测试系列之七测试报告

Powermock

官网:github.com/powermock/p…

简介

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的用法介绍完毕。