PowerMockito的使用

原创 2018年04月16日 14:02:11

PowerMockito是单元测试mock必备利器,你值得拥有.

本篇内容讲述了使用PowerMockito的原因以及如何使用它的API.

为什么Mockito不能mock静态方法

这也许是我要使用PowerMockito最大的原因了,因为项目中有很多场景都会用到静态方法,举例来说各种Utils工具类,还有Activity的跳转工具类,在单元测试中偶尔要验证这些工具类的方式是否调用,所以Mockito就有所局限,那它为什么不能mock静态方法呢?

通过各种搜罗发现,主要是因为Mockito的实现方式决定了它不能mock静态方法,首先它使用继承的方式实现mock对象,然后通过CGLIB生成mock对象来代替真实的对象进行执行,为了mock实例的方法,你可以在subclass中覆盖它,然而static方法是不能被子类覆盖的,所以Mockito不能mock静态方法.

CGLIB是一个功能强大,高性能的代码生成包。但是不能生成final方法.所以也就很好解释了,Mockito也不能mock final方法.

关于PowerMockito

我们知道Mockito不能mock静态, final和私有方法.当然不仅Mockito不支持,其他比较流行的mock框架都不支持这些特性.

PowerMock扩展了Mockito等模拟框架的API,通过Java的反射机制解决了上述不足.

下面内容将会介绍PowerMockito API以及它如何在测试中应用.

准备工作

本篇教程是以Mockito做模拟框架进行讲解.如果使用其他mock框架会有相应的不同,请自行参考官网: https://github.com/powermock/powermock/wiki

使用框架的第一步肯定需要引入, 以Android Studio为例,在moudle目录下的build.gradle中添加依赖:

// more
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    testImplementation 'org.powermock:powermock-module-junit4:1.7.1'
    testImplementation 'org.powermock:powermock-api-mockito2:1.7.1'
    testImplementation 'junit:junit:4.12'
    androidTestCompile 'org.mockito:mockito-android:2.8.9'
}

接下来,我们需要在我们的测试用例上使用下面PowerMockito提供的两个注解:

@RunWith(PowerMockRunner.class)
@PrepareForTest(fullyQualifiedNames = "com.lulu.androidtestdemo.mock.powerclass.*")

@PrepareForTest注解中的fullyQualifiedNames字段表示我们想要模拟的类的完全限定名,上面的例子中表示将com.lulu.androidtestdemo.mock.powerclass包下所有类都需要模拟.

当然也可以通过下面的形式指定需要模拟的类;

@PrepareForTest({CollaboratorWithFinalMethods.class, XXX})

注意: 务必参考官网(https://github.com/powermock/powermock/wiki/Getting-Started)中PowerMockito与JUnit和Mockito的版本对应关系.否则会抛出各种NoClassDefFoundError异常.

模拟构造方法和Final方法

本节我们将实现如何在使用new运算符实例化类时获取模拟实例而不是真实实例,然后使用这个模拟对象模拟final方法.

假设定义下面的CollaboratorWithFinalMethods类.(注意放在 src/main/java 文件夹下):

public class CollaboratorWithFinalMethods {
    public final String helloMethod() {
        return "Hello World!";
    }
}

首先我们通过PowerMockito API创建下面的mock对象:

CollaboratorWithFinalMethods mock = mock(CollaboratorWithFinalMethods.class);

接下来,设置期望, 当该类的无参数构造函数调用时应该返回一个mock实例而不是真实实例:

whenNew(CollaboratorWithFinalMethods.class).withNoArguments().thenReturn(mock);

让我们通过使用默认构造函数实例化CollaboratorWithFinalMethods类,然后验证PowerMock的行为,看看这个模拟构造函数是否生效:

CollaboratorWithFinalMethods collaborator = new CollaboratorWithFinalMethods();
verifyNew(CollaboratorWithFinalMethods.class).withNoArguments();

下一步我们设置一个 final 方法的期望:

when(collaborator.helloMethod()).thenReturn("Hello Lulu!");

执行这个方法:

String welcome = collaborator.helloMethod();

下面的断言验证了helloMethod方法已经通过collaborator对象调用,并返回了由PowerMock设置的期望值:

Mockito.verify(collaborator).helloMethod();
assertEquals("Hello Baeldung!", welcome);

如果我们想要模拟一个特定的对象的 final 方法,而不是所有 final 方法,Mockito.spy(T object)将会派上用场,后面会讲到.

模拟静态(static)方法

假设我们想要模拟名为CollaboratorWithStaticMethods类的静态方法.

CollaboratorWithStaticMethods类声明如下. (注意放在 src/main/java 文件夹下):

public class CollaboratorWithStaticMethods {
    public static String firstMethod(String name) {
        return "Hello " + name + " !";
    }

    public static String secondMethod() {
        return "Hello no one!";
    }

    public static String thirdMethod() {
        return "Hello no one again!";
    }
}

为了可以模拟这些静态方法,我们需要使用PowerMock的API注册这个类:

mockStatic(CollaboratorWithStaticMethods.class);

另外,我们可能会使用Mockito.spy(Class class)方法模拟特定的类,在下一小节中将会介绍.

接下来,可以设定方法被调用时返回的期望值:

when(CollaboratorWithStaticMethods.firstMethod(Mockito.anyString()))
  .thenReturn("Hello Lulu!");
when(CollaboratorWithStaticMethods.secondMethod()).thenReturn("Nothing special");

或者当调用thirdMethod 方法时抛出异常:

doThrow(new RuntimeException()).when(CollaboratorWithStaticMethods.class);
CollaboratorWithStaticMethods.thirdMethod();

现在去执行静态方法:

String firstWelcome = CollaboratorWithStaticMethods.firstMethod("Whoever");
String secondWelcome = CollaboratorWithStaticMethods.firstMethod("Whatever");

上面的调用不是调用的真实类的方法,而是委托给了模拟方法,以下断言证明了模拟已经生效:

assertEquals("Hello Baeldung!", firstWelcome);
assertEquals("Hello Baeldung!", secondWelcome);

我们还能够验证模拟方法的行为,包括方法的调用次数. 在这个案例中, firstMethod 被调用了两次,而secondMethod从来没有被调用过:

verifyStatic(CollaboratorWithStaticMethods.class, Mockito.times(2));
CollaboratorWithStaticMethods.firstMethod(Mockito.anyString());

verifyStatic(CollaboratorWithStaticMethods.class, Mockito.never());
CollaboratorWithStaticMethods.secondMethod();

注意: verifyStatic方法必须在需要验证的静态方法之前调用,这样才能让PowerMockito知道我们要验证的静态方法是哪一个.

最后,验证静态thirdMethod 方法当被调用时应改抛出RuntimeException异常.需要通过@Test注解声明所抛出的异常:

@Test(expected = RuntimeException.class)
public void testStaticMethod() {
    // other methods   

    CollaboratorWithStaticMethods.thirdMethod();
}

模拟部分方法

PowerMockito可以通过使用spy方法来模拟一个类中的部分方法,而不是整个类.首先声明下面的类(注意放在 src/main/java 文件夹下):

public class CollaboratorForPartialMocking {
    public static String staticMethod() {
        return "Hello Lulu!";
    }

    public final String finalMethod() {
        return "Hello Lulu!";
    }

    private String privateMethod() {
        return "Hello Lulu!";
    }

    public String privateMethodCaller() {
        return privateMethod() + " Welcome to the Java world.";
    }
}

让我们模拟该类中的静态方法staticMethod, 首先使用PowerMockito API 部分模拟CollaboratorForPartialMocking类并设置静态方法的期望值:

spy(CollaboratorForPartialMocking.class);
when(CollaboratorForPartialMocking.staticMethod()).thenReturn("I am a static mock method.");

执行这个静态方法:

returnValue = CollaboratorForPartialMocking.staticMethod();

接下来验证模拟的行为:

verifyStatic(CollaboratorWithFinalMethods.class);
CollaboratorForPartialMocking.staticMethod();

下面的断言语句证验证了模拟方法返回了期望值:

assertEquals("I am a static mock method.", returnValue);

现在,我们开始使用final和private方法.为了演示这些方法被部分模拟,我们需要通过使用PowerMockito API 的spy 初始化这个类:

CollaboratorForPartialMocking collaborator = new CollaboratorForPartialMocking();
CollaboratorForPartialMocking mock = spy(collaborator);

上面创建的对象用于演示final和private方法的模拟.

现在我们通过设定期望值并调用这个方法来处理final方法:

when(mock.finalMethod()).thenReturn("I am a final mock method.");
returnValue = mock.finalMethod();

证明部分模拟方法的行为:

Mockito.verify(mock).finalMethod();

测试验证调用finalMethod方法将返回一个符合期望值的值:

assertEquals("I am a final mock method.", returnValue);

对于private方法的验证也是类似的过程.主要区别在于我们不能直接从测试用例中调用此方法.基本上private方法只能由同一个类调用.在CollaboratorForPartialMocking类中privateMethod方法将由privateMethodCaller调用,我们将使用后者作为委托.现在开始设置期望值并且调用这个方法:

when(mock, "privateMethod").thenReturn("I am a private mock method.");
returnValue = mock.privateMethodCaller();

验证模拟的private方法:

verifyPrivate(mock).invoke("privateMethod");

下面测试时确保调用private方法的返回值与期望值相同:

assertEquals("I am a private mock method. Welcome to the Java world.", returnValue);

上面的案例中,通过spy我们没有模拟这个类中的所有方法,而只是模拟了privateMethod方法.也就是说mock.privateMethodCaller()调用了真实的方法而不是模拟方法.

结束语

本篇文章介绍了PowerMockito API, 解决开发人员在使用Mockito框架时遇到的一些问题.关注公众号可以获取所有源码.

敲行代码再睡觉

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013144863/article/details/79959773

PowerMockito使用详解

一、为什么要使用Mock工具       在做单元测试的时候,我们会发现我们要测试的方法会引用很多外部依赖的对象,比如:(发送邮件,网络通讯,远程服务, 文件系统等等)。 而我们没法控制这些外部...
  • knighttools
  • knighttools
  • 2015-03-25 23:33:20
  • 28961

PowerMockito的简单的介绍

我们的依赖的配置 1.5.6 org.powermock powermock-module-junit4
  • u012881904
  • u012881904
  • 2016-05-06 21:24:52
  • 3476

Mockito&PowerMockito实战

Mockito&PowerMockito的使用, 业务层单元测试
  • Extra_warrior
  • Extra_warrior
  • 2016-09-21 18:07:54
  • 2429

PowerMockito的基本使用记录

PowerMockito自己常用使用记录 Note:在使用PowerMockito的过程中,如果我要同时mock2个static类,貌似就会出现moc...
  • cloud_ll
  • cloud_ll
  • 2015-04-26 15:16:24
  • 1832

使用Powermock和mockito来进行单元测试

简介 Mockito是一个流行的Mocking框架。它使用起来简单,学习成本很低,而且具 有非常简洁的API,测试代码的可读性很高。因此它十分受欢迎,用户群越来越 多,很多的开源的软件也选择了M...
  • u013428664
  • u013428664
  • 2015-03-06 10:01:32
  • 4182

PowerMockito实践<二>

概述: PowerMockito擅长mock静态类/静态方法/私有方法/构造器 1. 当给spy的类设桩时,最好使用doReturn / doThrow / doNothing(只适应于返回voi...
  • Extra_warrior
  • Extra_warrior
  • 2016-12-27 16:06:54
  • 1443

基于Spring的PowerMockito TestCase

很多J2EE应用都是基于Spring的,一些比较复杂的测试用例,如果用基于TestCase比较难满足全部的测试场景。 所以,我们会用到一些Mock工具,如PowerMockito: org.po...
  • jason5186
  • jason5186
  • 2015-01-15 15:46:52
  • 4715

使用Powermock和Mockito测试静态方法

使用Powermock和Mockito测试静态方法 转自:http://heipark.iteye.com/blog/1666681 1. 加入依赖包(maven) org.powerm...
  • shyu1989
  • shyu1989
  • 2013-12-19 22:57:10
  • 4130

PowerMockito单元测试陷井一例

今天写单元测试碰到一个情况,被测试类Target的成员变量的值需要mock private aaDao = AA.getManager().getDao(); 类似这样的,我需要把AA.getMa...
  • ynwso
  • ynwso
  • 2016-09-30 15:54:51
  • 1127

基于TestNG使用PowerMock的Mockito扩展在Maven测试项目中的配置说明

我们知道,PowerMock也提供了对TestNG的支持 在Maven测试项目中,基于TestNG配置pom.xml文件以使用PowerMock的Mockito扩展,示例如下: 1....
  • taiyangdao
  • taiyangdao
  • 2016-10-25 23:24:44
  • 1553
收藏助手
不良信息举报
您举报文章:PowerMockito的使用
举报原因:
原因补充:

(最多只允许输入30个字)