PowerMockito的使用

PowerMockito的使用PowerMockito 是单元测试 mock 必备利器 你值得拥有 本篇内容讲述了使用 PowerMockito 的原因以及如何使用它的 API 为什么 Mockito 不能 mock 静态方法这也许是我要使用 PowerMockito 最大的原因了 因为项目中有很多场景都会用到静态方法 举例来说各种 Utils 工具类 还有 Activity 的跳转工具类 在单元测试中偶尔要验证这些工具类的方

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://javaforall.net/214964.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午3:01
下一篇 2026年3月18日 下午3:01


相关推荐

  • Chrome断点调试

    Chrome断点调试1.断点调试是啥?难不难?断点调试其实并不是多么复杂的一件事,简单的理解无外呼就是打开浏览器,打开sources找到js文件,在行号上点一下罢了。操作起来似乎很简单,其实很多人纠结的是,是在哪里打断点?(我们先看一个断点截图,以chrome浏览器的断点为例)步骤记住没?用chrome浏览器打开页面→按f12打开开发者工具→打开Sources→打开你要调试的js代码文件→在行号上单击…

    2022年5月22日
    191
  • python isinstance函数_Python isinstance函数介绍

    python isinstance函数_Python isinstance函数介绍isinstance object classinfo 判断实例是否是这个类或者 objectobject 是变量 classinfo 是类型 tuple dict int float 判断变量是否是这个类型复制代码代码如下 classobjA passA objA B a v C astring printisinsta A objA printisinsta

    2025年9月26日
    5
  • pycharm如何设置背景图片_python配置pycharm

    pycharm如何设置背景图片_python配置pycharm工具栏也是有背景图的

    2022年8月25日
    7
  • Spring AOP面向切面编程:理解篇(一看就明白)

    Spring AOP面向切面编程:理解篇(一看就明白)一直想着怎么去通俗的讲解 AOP 看了一篇文章受到了启发 https blog csdn net qukaiwei article details 下面我加入自己的理解 咱们来说说 AOP 一 到底什么是 AOP 面向切面编程 无论在学习或者面试的时候 大家都会张口说 spring 的特性 AOP 和 IOC 控制反转咱们下一篇讲 有些大神理解的很到位 但是对于大多数初中级工程

    2026年3月20日
    2
  • 2021DIY电脑配置入门篇(包含各cpu显卡天梯图对比)

    2021DIY电脑配置入门篇(包含各cpu显卡天梯图对比)前言:我本来以为一篇文章可以把电脑配置讲清楚的,但是发现电脑比我想象的要复杂,所以可能分了几篇来写如何查看自己的电脑配置最简单的右键桌面此电脑->点击属性下载个电脑管家等电脑助手软件也可以查看详细配置如何DIY自己的第一台电脑篇幅有限,这里我只详细分析一台电脑的核心配置(CPU、主板、显卡),外加内存定好预算对于电脑来说,预算是最重要的!没有预算,一切都是空谈。没预算默认外星人Area51M(价格在2万左右),现在电脑往往充当一种娱乐需求,相对来说比较次要,因此大多数人配电脑.

    2022年7月12日
    31
  • jvmxmx和xms参数分析(设定优化校准)

    XmnXmsXmxXss有什么区别Xmn、Xms、Xmx、Xss都是JVM对内存的配置参数,我们可以根据不同需要区修改这些参数,以达到运行程序的最好效果。-Xms堆内存的最小大小,默认为物理内存的1/64-Xmx堆内存的最大大小,默认为物理内存的1/4-Xmn堆内新生代的大小。通过这个值也可以得到老生代的大小:-Xmx减去-Xmn-Xss设置每个线程可使用的内存大小,即…

    2022年4月18日
    48

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号