之前的两篇文章分别说到了 JUnit 与 Mockito ,一个主要用于断言,一个用于模拟我们的非测试类。今天说一说PowerMock ,它拓展了
Mockito
框架,从而支持了mock static方法、private方法、final方法与类等等。(这里八卦一下,为什么Mockito
不自己支持 private 方法?有兴趣的自行查看原因)
1.PowerMock选择
PowerMock
主要围绕着 Junit
测试框架和 TestNg
测试框架,在安卓中我们常用Junit + Mockito + PowerMock
组合。既然PowerMock
是拓展Mockito
,所以使用时,我们要找到与Mockito
对应的PowerMock
支持版本去使用。
这里我们使用如下对应版本:
testCompile "org.mockito:mockito-core:2.8.9"
testCompile "org.powermock:powermock-module-junit4:1.7.3"
testCompile "org.powermock:powermock-module-junit4-rule:1.7.3"
testCompile "org.powermock:powermock-api-mockito2:1.7.3" //注意这里是mockito2
testCompile "org.powermock:powermock-classloading-xstream:1.7.3"
2.PowerMock使用
首先我们定义一个Fruit
类,Banana
继承于它。其中有我们后面需要mock的static、private等方法。
abstract class Fruit {
private String fruit = "水果";
public String getFruit() {
return fruit;
}
}
public class Banana extends Fruit{
private static String COLOR = "黄色的";
public Banana() {}
public static String getColor() {
return COLOR;
}
public String getBananaInfo() {
return flavor() + getColor();
}
private String flavor() {
return "甜甜的";
}
public final boolean isLike() {
return true;
}
}
1.mock静态方法
@RunWith(PowerMockRunner.class)
public class PowerMockitoStaticMethodTest {
@Test
@PrepareForTest({Banana.class})
public void testStaticMethod() {
PowerMockito.mockStatic(Banana.class); //<-- mock静态类
Mockito.when(Banana.getColor()).thenReturn("绿色");
Assert.assertEquals("绿色", Banana.getColor());
}
}
首先使用PowerMock
必须加注解@PrepareForTest
和@RunWith(PowerMockRunner.class)
。注解@PrepareForTest
里写的是静态方法所在的类。
如果我们要更改类的私有static常量,比如修改Banana
中的COLOR
。
@Test
@PrepareForTest({Banana.class})
public void testChangeColor() {
Whitebox.setInternalState(Banana.class, "COLOR", "红色的");
Assert.assertEquals("红色的", Banana.getColor());
}
2.mock私有方法
@RunWith(PowerMockRunner.class)
public class PowerMockitoPrivateMethodTest {
@Test
@PrepareForTest({Banana.class})
public void testPrivateMethod() throws Exception {
Banana mBanana = PowerMockito.mock(Banana.class);
PowerMockito.when(mBanana.getBananaInfo()).thenCallRealMethod();
PowerMockito.when(mBanana, "flavor").thenReturn("苦苦的");
Assert.assertEquals("苦苦的黄色的", mBanana.getBananaInfo());
//验证flavor是否调用了一次
PowerMockito.verifyPrivate(mBanana).invoke("flavor");
}
}
我们通过mock私有方法flavor
,使得之前的“甜甜的”变为了“苦苦的”。当然我们也可以跳过私有方法,代码如下:
@Test
@PrepareForTest({Banana.class})
public void skipPrivateMethod() {
Banana mBanana = new Banana();
//跳过flavor方法
PowerMockito.suppress(PowerMockito.method(Banana.class, "flavor"));
Assert.assertEquals("null黄色的", mBanana.getBananaInfo());
}
因为跳过了flavor
方法,最终输出结果为“null黄色的”。
如果我们要更改更改父类私有变量,比如修改Fruit
中的fruit
。
@Test
@PrepareForTest({Banana.class})
public void testChangeParentPrivate() throws Exception {
Banana mBanana = new Banana();
MemberModifier.field(Banana.class, "fruit").set(mBanana, "蔬菜");
Assert.assertEquals("蔬菜", mBanana.getFruit());
}
3.mock final方法
@RunWith(PowerMockRunner.class)
public class PowerMockitoFinalMethodTest {
@Test
@PrepareForTest({Banana.class})
public void testFinalMethod() throws Exception {
Banana mBanana = PowerMockito.mock(Banana.class);
PowerMockito.when(mBanana.isLike()).thenReturn(false);
Assert.assertFalse(mBanana.isLike());
}
}
使用方法和使用mockito
一样,但是我们通过PowerMock
,成功修改了isLike
方法的返回值。
4.mock 构造方法
@Test
@PrepareForTest({Banana.class})
public void testNewClass() throws Exception {
Banana mBanana = PowerMockito.mock(Banana.class);
PowerMockito.when(mBanana.getBananaInfo()).thenReturn("大香蕉");
//如果new新对象,则返回这个上面设置的这个对象
PowerMockito.whenNew(Banana.class).withNoArguments().thenReturn(mBanana);
//new新的对象
Banana newBanana = new Banana();
Assert.assertEquals("大香蕉", newBanana.getBananaInfo());
}
whenNew
方法的意思是之后 new 这个对象时,返回某个被 Mock 的对象而不是让真的 new 新的对象。如果构造方法有参数,可以在withNoArguments
方法中传入。
5.其他
上面我们有说到使用PowerMock
就必须加@RunWith(PowerMockRunner.class)
,但是我们毕竟有时会使用多个测试框架,可能@RunWith
会占用。这时我们可以使用@Rule
。代码如下:
@Rule
public PowerMockRule rule = new PowerMockRule();
记住使用这种方法需要加入依赖:
testCompile "org.powermock:powermock-module-junit4-rule:1.7.3"
testCompile "org.powermock:powermock-classloading-xstream:1.7.3"
本篇所有代码已上传至Github。希望大家多多点赞支持!