PowerMockito的使用

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

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框架时遇到的一些问题.关注公众号可以获取所有源码.

敲行代码再睡觉

阅读更多 登录后自动展开
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页