`

PowerMock 简介

阅读更多
EasyMock 以及 Mockito 都因为可以极大地简化单元测试的书写过程而被许多人应用在自己的工作中,但是这 2 种 Mock 工具都不可以实现对静态函数、构造函数、私有函数、Final 函数以及系统函数的模拟,但是这些方法往往是我们在大型系统中需要的功能。PowerMock 是在 EasyMock 以及 Mockito 基础上的扩展,通过定制类加载器等技术,PowerMock 实现了之前提到的所有模拟功能,使其成为大型系统上单元测试中的必备工具。
单元测试模拟框架的功能及其实现简介
单元测试在软件开发过程中的重要性不言而喻,特别是在测试驱动开发的开发模式越来越流行的前提下,单元测试更成为了软件开发过程中不可或缺的部分。于是相应的,各种单元测试技术也应运而生。本文要介绍的 PowerMock 以及 Mockito 都是简化单元测试书写过程的工具。
Mockito 是一个针对 Java 的单元测试模拟框架,它与 EasyMock 和 jMock 很相似,都是为了简化单元测试过程中测试上下文 ( 或者称之为测试驱动函数以及桩函数 ) 的搭建而开发的工具。在有这些模拟框架之前,为了编写某一个函数的单元测试,程序员必须进行十分繁琐的初始化工作,以保证被测试函数中使用到的环境变量以及其他模块的接口能返回预期的值,有些时候为了单元测试的可行性,甚至需要牺牲被测代码本身的结构。单元测试模拟框架则极大的简化了单元测试的编写过程:在被测试代码需要调用某些接口的时候,直接模拟一个假的接口,并任意指定该接口的行为。这样就可以大大的提高单元测试的效率以及单元测试代码的可读性。
相对于 EasyMock 和 jMock,Mockito 的优点是通过在执行后校验哪些函数已经被调用,消除了对期望行为(expectations)的需要。其它的 mocking 库需要在执行前记录期望行为(expectations),而这导致了丑陋的初始化代码。
但是,Mockito 也并不是完美的,它不提供对静态方法、构造方法、私有方法以及 Final 方法的模拟支持。而程序员时常都会发现自己有对以上这些方法的模拟需求,特别是当一个已有的软件系统摆在面前时。幸好 , 还有 PowerMock。
PowerMock 也是一个单元测试模拟框架,它是在其它单元测试模拟框架的基础上做出的扩展。通过提供定制的类加载器以及一些字节码篡改技巧的应用,PowerMock 现了对静态方法、构造方法、私有方法以及 Final 方法的模拟支持,对静态初始化过程的移除等强大的功能。因为 PowerMock 在扩展功能时完全采用和被扩展的框架相同的 API, 熟悉 PowerMock 所支持的模拟框架的开发者会发现 PowerMock 非常容易上手。PowerMock 的目的就是在当前已经被大家所熟悉的接口上通过添加极少的方法和注释来实现额外的功能,目前,PowerMock 仅支持 EasyMock 和 Mockito。
本文的目的就是和大家一起学习在 Mockito 框架上扩展的 PowerMock 的强大功能。
回页首
环境配置方法
对于需要的开发包,PowerMock 网站提供了”一站式”下载 : 从 此页面中选择以类似 PowerMock 1.4.10 with Mockito and JUnit including dependencies 为注释的链接,该包中包含了最新的 JUnit 库,Mockito 库,PowerMock 库以及相关的依赖。
如果是使用 Eclipse 开发,只需要在 Eclipse 工程中包含这些库文件即可。
如果是使用 Maven 开发,则需要根据版本添加以下清单内容到 POM 文件中:
JUnit 版本 4.4 以上请参考清单 1,

清单 1

<properties>
    <powermock.version>1.4.10</powermock.version>
</properties>
<dependencies>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit4</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
</dependencies>

JUnit 版本 4.0-4.3 请参考清单 2,

清单 2

<properties>
    <powermock.version>1.4.10</powermock.version>
</properties>
<dependencies>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit4-legacy</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
</dependencies>

JUnit 版本 3 请参考清单 3,

清单 3

<properties>
    <powermock.version>1.4.10</powermock.version>
</properties>
<dependencies>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit3</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
</dependencies>

回页首
PowerMock 在单元测试中的应用
模拟 Static 方法
在任何需要用到 PowerMock 的类开始之前,首先我们要做如下声明:
@RunWith(PowerMockRunner.class)
然后,还需要用注释的形式将需要测试的静态方法提供给 PowerMock:
@PrepareForTest( { YourClassWithEgStaticMethod.class })
然后就可以开始写测试代码:
首先,需要有一个含有 static 方法的代码 , 如清单 4:

清单 4

public class IdGenerator {

    ...
   
    public static long generateNewId() {
        ...
    }
   
    ...
}

然后,在被测代码中,引用了以上方法 , 如清单 5 所示:

清单 5

public class ClassUnderTest {
    ...
    public void methodToTest() {
        ..
        final long id = IdGenerator.generateNewId();
        ..
     }
    ...
}

为了达到单元测试的目的,需要让静态方法 generateNewId()返回各种值来达到对被测试方法 methodToTest()的覆盖测试,实现方式如清单 6 所示:

清单 6

@RunWith(PowerMockRunner.class)
//We prepare the IdGenerator for test because the static method is normally not mockable
@PrepareForTest(IdGenerator.class)
public class MyTestClass {
    @Test
    public void demoStaticMethodMocking() throws Exception {
        mockStatic(IdGenerator.class);
        /*
         * Setup the expectation using the standard Mockito syntax,
         * generateNewId() will now return 2 everytime it's invoked
         * in this test.
         */
        when(IdGenerator.generateNewId()).thenReturn(2L);

        new ClassUnderTest().methodToTest();

        // Optionally verify that the static method was actually called
        verifyStatic();
        IdGenerator.generateNewId();
    }
}

如清单 6 中所展示,在测试代码中,可以使用 When().thenReturn() 语句来指定被引用的静态方法返回任意需要的值,达到覆盖测试的效果。
模拟构造函数
有时候,能模拟构造函数,从而使被测代码中 new 操作返回的对象可以被随意定制,会很大程度的提高单元测试的效率,考虑如清单 7 的代码:

清单 7

public class DirectoryStructure {
    public boolean create(String directoryPath) {
        File directory = new File(directoryPath);

        if (directory.exists()) {
            throw new IllegalArgumentException(
            "\"" + directoryPath + "\" already exists.");
        }

        return directory.mkdirs();
    }
}

为了充分测试 create()函数,我们需要被 new 出来的 File 对象返回文件存在和不存在两种结果。在 PowerMock 出现之前,实现这个单元测试的方式通常都会需要在实际的文件系统中去创建对应的路径以及文件。然而,在 PowerMock 的帮助下,本函数的测试可以和实际的文件系统彻底独立开来:使用 PowerMock 来模拟 File 类的构造函数,使其返回指定的模拟 File 对象而不是实际的 File 对象,然后只需要通过修改指定的模拟 File 对象的实现,即可实现对被测试代码的覆盖测试,参考如清单 8 的代码:

清单 8

@RunWith(PowerMockRunner.class)
@PrepareForTest(DirectoryStructure.class)
public class DirectoryStructureTest {
    @Test
    public void createDirectoryStructureWhenPathDoesntExist() throws Exception {
        final String directoryPath = "mocked path";

        File directoryMock = mock(File.class);

        // This is how you tell PowerMockito to mock construction of a new File.
        whenNew(File.class).withArguments(directoryPath).thenReturn(directoryMock);

        // Standard expectations
        when(directoryMock.exists()).thenReturn(false);
        when(directoryMock.mkdirs()).thenReturn(true);

        assertTrue(new NewFileExample().createDirectoryStructure(directoryPath));

        // Optionally verify that a new File was "created".
        verifyNew(File.class).withArguments(directoryPath);
    }
}

使用 whenNew().withArguments().thenReturn() 语句即可实现对具体类的构造函数的模拟操作。然后对于之前创建的模拟对象 directoryMock使用 When().thenReturn() 语句,即可实现需要的所有功能,从而实现对被测对象的覆盖测试。在本测试中,因为实际的模拟操作是在类 DirectoryStructureTest 中实现,所以需要指定的 @PrepareForTest 对象是 DirectoryStructureTest.class。
模拟私有以及 Final 方法
为了实现对类的私有方法或者是 Final 方法的模拟操作,需要 PowerMock 提供的另外一项技术:局部模拟。
在之前的介绍的模拟操作中,我们总是去模拟一整个类或者对象,然后使用 When().thenReturn()语句去指定其中值得关心的部分函数的返回值,从而达到搭建各种测试环境的目标。对于没有使用 When().thenReturn()方法指定的函数,系统会返回各种类型的默认值(具体值可参考官方文档)。
局部模拟则提供了另外一种方式,在使用局部模拟时,被创建出来的模拟对象依然是原系统对象,虽然可以使用方法 When().thenReturn()来指定某些具体方法的返回值,但是没有被用此函数修改过的函数依然按照系统原始类的方式来执行。
这种局部模拟的方式的强大之处在于,除开一般方法可以使用之外,Final 方法和私有方法一样可以使用。
参考如清单 9 所示的被测代码:

清单 9

public final class PrivatePartialMockingExample {
    public String methodToTest() {
        return methodToMock("input");
    }

    private String methodToMock(String input) {
        return "REAL VALUE = " + input;
    }
}

为了保持单元测试的纯洁性,在测试方法 methodToTest()时,我们不希望受到私有函数 methodToMock()实现的干扰,为了达到这个目的,我们使用刚提到的局部模拟方法来实现 , 实现方式如清单 10:

清单 10

@RunWith(PowerMockRunner.class)
@PrepareForTest(PrivatePartialMockingExample.class)
public class PrivatePartialMockingExampleTest {
    @Test
    public void demoPrivateMethodMocking() throws Exception {
        final String expected = "TEST VALUE";
        final String nameOfMethodToMock = "methodToMock";
        final String input = "input";

        PrivatePartialMockingExample underTest = spy(new PrivatePartialMockingExample());

        /*
         * Setup the expectation to the private method using the method name
         */
        when(underTest, nameOfMethodToMock, input).thenReturn(expected);

        assertEquals(expected, underTest.methodToTest());

        // Optionally verify that the private method was actually called
        verifyPrivate(underTest).invoke(nameOfMethodToMock, input);
    }
}

可以发现,为了实现局部模拟操作,用来创建模拟对象的函数从 mock() 变成了 spy(),操作对象也从类本身变成了一个具体的对象。同时,When() 函数也使用了不同的版本:在模拟私有方法或者是 Final 方法时,When() 函数需要依次指定模拟对象、被指定的函数名字以及针对该函数的输入参数列表。
回页首
结束语
以上列举了扩展于 Mockito 版本的 PowerMock 的一部分强大的功能,特别是针对已有的软件系统,利用以上功能可以轻易的完成清晰独立的单元测试代码,帮助我们提高代码质量。
回页首
注:
本文中的部分测试代码引用自 Johan Haleby 的 Untestable code with Mockito and PowerMock。

参考资料
学习
Mockito 官方网站:Mockito 的官方站点 , 在这里你可以找到完整的 Mockito 介绍、接口文档,以及一些代码示例。

PowerMock 官方网站:PowerMock 的官方站点 , 在这里你可以找到完整的 PowerMock 介绍、接口文档,以及一些代码示例。

developerWorks Java 技术专区:这里有数百篇关于 Java 编程各个方面的文章。

讨论
加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。

作者简介
张羽是一位拥有 6 年从业经验的软件开发人员。在最近的 4 年里,他从事于 Java 和 C++ 的开发工作。
吴长侠工作在国际商业机器(中国)投资有限公司(IBM China)中国系统和技术实验室。中国科学技术大学硕士,目前是 IBM Systems Director 的测试组成员。
分享到:
评论

相关推荐

    AI从头到脚详解如何创建部署Azure Web App的OpenAI项目源码

    【AI】从头到脚详解如何创建部署Azure Web App的OpenAI项目源码

    人脸识别_卷积神经网络_CNN_ORL数据库_身份验证_1741779511.zip

    人脸识别项目实战

    人工智能-人脸识别代码

    人工智能-人脸识别代码,采用cnn的架构识别代码

    汽车配件制造业企业信息化整体解决方案.pptx

    汽车配件制造业企业信息化整体解决方案

    短期风速预测模型,IDBO-BiTCN-BiGRU-Multihead-Attention IDBO是,网上复现 评价指标:R方、MAE、MAPE、RMSE 附带测试数据集运行(风速数据) 提示:在

    短期风速预测模型,IDBO-BiTCN-BiGRU-Multihead-Attention IDBO是,网上复现 评价指标:R方、MAE、MAPE、RMSE 附带测试数据集运行(风速数据) 提示:在MATLAB2024a上测试正常 ,短期风速预测模型; IDBO-BiTCN-BiGRU-Multihead-Attention; 评价指标: R方、MAE、MAPE、RMSE; 复现; 测试数据集; MATLAB 2024a,短期风速预测模型:IDBO-BiTCN-BiGRU-Attention集成模型

    手势识别_数据融合_运动融合帧_Pytorch实现_1741857761.zip

    手势识别项目实战

    智慧园区IBMS可视化管理系统建设方案PPT(61页).pptx

    在智慧园区建设的浪潮中,一个集高效、安全、便捷于一体的综合解决方案正逐步成为现代园区管理的标配。这一方案旨在解决传统园区面临的智能化水平低、信息孤岛、管理手段落后等痛点,通过信息化平台与智能硬件的深度融合,为园区带来前所未有的变革。 首先,智慧园区综合解决方案以提升园区整体智能化水平为核心,打破了信息孤岛现象。通过构建统一的智能运营中心(IOC),采用1+N模式,即一个智能运营中心集成多个应用系统,实现了园区内各系统的互联互通与数据共享。IOC运营中心如同园区的“智慧大脑”,利用大数据可视化技术,将园区安防、机电设备运行、车辆通行、人员流动、能源能耗等关键信息实时呈现在拼接巨屏上,管理者可直观掌握园区运行状态,实现科学决策。这种“万物互联”的能力不仅消除了系统间的壁垒,还大幅提升了管理效率,让园区管理更加精细化、智能化。 更令人兴奋的是,该方案融入了诸多前沿科技,让智慧园区充满了未来感。例如,利用AI视频分析技术,智慧园区实现了对人脸、车辆、行为的智能识别与追踪,不仅极大提升了安防水平,还能为园区提供精准的人流分析、车辆管理等增值服务。同时,无人机巡查、巡逻机器人等智能设备的加入,让园区安全无死角,管理更轻松。特别是巡逻机器人,不仅能进行360度地面全天候巡检,还能自主绕障、充电,甚至具备火灾预警、空气质量检测等环境感知能力,成为了园区管理的得力助手。此外,通过构建高精度数字孪生系统,将园区现实场景与数字世界完美融合,管理者可借助VR/AR技术进行远程巡检、设备维护等操作,仿佛置身于一个虚拟与现实交织的智慧世界。 最值得关注的是,智慧园区综合解决方案还带来了显著的经济与社会效益。通过优化园区管理流程,实现降本增效。例如,智能库存管理、及时响应采购需求等举措,大幅减少了库存积压与浪费;而设备自动化与远程监控则降低了维修与人力成本。同时,借助大数据分析技术,园区可精准把握产业趋势,优化招商策略,提高入驻企业满意度与营收水平。此外,智慧园区的低碳节能设计,通过能源分析与精细化管理,实现了能耗的显著降低,为园区可持续发展奠定了坚实基础。总之,这一综合解决方案不仅让园区管理变得更加智慧、高效,更为入驻企业与员工带来了更加舒适、便捷的工作与生活环境,是未来园区建设的必然趋势。

    相亲交友系统源码 V10.5支持婚恋相亲M红娘系统.zip

    相亲交友系统源码 V10.5支持婚恋相亲、媒婆返利、红娘系统、商城系统等等 这款交友系统功能太多了,适合婚恋相亲,还有媒婆婚庆等等支持 PC和 H5还有小程序,可封装红年、APP,里面带安装教程

    单片机也能玩双核之你想不到c技巧系列-嵌入式实战(资料+视频教程)

    本资源《单片机也能玩双核之你想不到的C技巧系列——嵌入式实战》涵盖 双核单片机开发、C语言高级技巧、嵌入式系统优化 等核心内容,结合 实战案例与视频教程,帮助开发者深入理解并掌握高效编程技巧。 适用人群: 适合 嵌入式开发工程师、单片机开发者、电子信息相关专业学生,以及希望提升 C语言编程能力 和 嵌入式项目经验 的技术人员。 能学到什么: 双核单片机开发思路,提高并行处理能力。 C语言高级技巧,提升代码优化与执行效率。 嵌入式系统调试方法,掌握实际项目中的调试策略。 实战案例解析,学习如何在实际工程中应用双核技术。 阅读建议: 建议 先学习基础知识,再结合 示例代码与视频教程 进行实操,重点关注 代码优化、调试技巧与双核应用模式,通过实战演练提高嵌入式开发能力。

    计算机视觉_OpenCV_人脸识别_成本节约检测方案_1741779495.zip

    人脸识别项目源码实战

    `机器学习_深度学习_Keras_教程用途`.zip

    人脸识别项目源码实战

    地铁网络_Dijkstra_最短路径_查询工具_1741862725.zip

    c语言学习

    红外光伏缺陷目标检测模型,YOLOv8模型 基于红外光伏缺陷目标检测数据集训练,做了必要的数据增强处理,以达到缺陷类别间的平衡 可检测大面积热斑,单一热斑,二极管短路和异常低温四类缺陷 测试集指标如

    红外光伏缺陷目标检测模型,YOLOv8模型 基于红外光伏缺陷目标检测数据集训练,做了必要的数据增强处理,以达到缺陷类别间的平衡 可检测大面积热斑,单一热斑,二极管短路和异常低温四类缺陷 测试集指标如图所示 ,核心关键词:红外光伏缺陷目标检测模型; YOLOv8模型; 数据增强处理; 缺陷类别平衡; 大面积热斑; 单一热斑; 二极管短路; 异常低温。,基于YOLOv8的红外光伏缺陷检测模型

    基于PLC的自动浇花控制系统 西门子1200PLC博途仿真,提供HMI画面,接线图,IO分配表,演示视频,简单讲解视频 博图15.1及以上版本均可使用 ,核心关键词: PLC自动浇花控制系统; 西

    基于PLC的自动浇花控制系统 西门子1200PLC博途仿真,提供HMI画面,接线图,IO分配表,演示视频,简单讲解视频 博图15.1及以上版本均可使用 ,核心关键词: PLC自动浇花控制系统; 西门子1200PLC博途仿真; HMI画面; 接线图; IO分配表; 演示视频; 简单讲解视频; 博图15.1及以上版本。,基于PLC的自动浇花系统:西门子1200PLC博途仿真实践教程

    智慧园区标准化综合解决方案PPT(60页).pptx

    在智慧园区建设的浪潮中,一个集高效、安全、便捷于一体的综合解决方案正逐步成为现代园区管理的标配。这一方案旨在解决传统园区面临的智能化水平低、信息孤岛、管理手段落后等痛点,通过信息化平台与智能硬件的深度融合,为园区带来前所未有的变革。 首先,智慧园区综合解决方案以提升园区整体智能化水平为核心,打破了信息孤岛现象。通过构建统一的智能运营中心(IOC),采用1+N模式,即一个智能运营中心集成多个应用系统,实现了园区内各系统的互联互通与数据共享。IOC运营中心如同园区的“智慧大脑”,利用大数据可视化技术,将园区安防、机电设备运行、车辆通行、人员流动、能源能耗等关键信息实时呈现在拼接巨屏上,管理者可直观掌握园区运行状态,实现科学决策。这种“万物互联”的能力不仅消除了系统间的壁垒,还大幅提升了管理效率,让园区管理更加精细化、智能化。 更令人兴奋的是,该方案融入了诸多前沿科技,让智慧园区充满了未来感。例如,利用AI视频分析技术,智慧园区实现了对人脸、车辆、行为的智能识别与追踪,不仅极大提升了安防水平,还能为园区提供精准的人流分析、车辆管理等增值服务。同时,无人机巡查、巡逻机器人等智能设备的加入,让园区安全无死角,管理更轻松。特别是巡逻机器人,不仅能进行360度地面全天候巡检,还能自主绕障、充电,甚至具备火灾预警、空气质量检测等环境感知能力,成为了园区管理的得力助手。此外,通过构建高精度数字孪生系统,将园区现实场景与数字世界完美融合,管理者可借助VR/AR技术进行远程巡检、设备维护等操作,仿佛置身于一个虚拟与现实交织的智慧世界。 最值得关注的是,智慧园区综合解决方案还带来了显著的经济与社会效益。通过优化园区管理流程,实现降本增效。例如,智能库存管理、及时响应采购需求等举措,大幅减少了库存积压与浪费;而设备自动化与远程监控则降低了维修与人力成本。同时,借助大数据分析技术,园区可精准把握产业趋势,优化招商策略,提高入驻企业满意度与营收水平。此外,智慧园区的低碳节能设计,通过能源分析与精细化管理,实现了能耗的显著降低,为园区可持续发展奠定了坚实基础。总之,这一综合解决方案不仅让园区管理变得更加智慧、高效,更为入驻企业与员工带来了更加舒适、便捷的工作与生活环境,是未来园区建设的必然趋势。

    大型集团用户画像系统化标准化数字化用户主数据管理项目规划方案.pptx

    大型集团用户画像系统化标准化数字化用户主数据管理项目规划方案

    基于STM32的水质 浊度检测仪设计与实现(详细设计说明书+ 10008-基于STM32的水质 浊度检测仪设计与实现(详细设计说明书+原理图PCB工程+源码工程+实物照片) 本次设计是设计一款水质检

    基于STM32的水质 浊度检测仪设计与实现(详细设计说明书+ 10008-基于STM32的水质 浊度检测仪设计与实现(详细设计说明书+原理图PCB工程+源码工程+实物照片) 本次设计是设计一款水质检测设备,实现温度检查、水质检测的功能,将检测到的数据显示到显示器中,并实时记录系统的参数 本次系统需要对温度检测,使用的传感器为DS18B20,通过单总线的方式来完成系统温度检测 使用水质检测模块检查水的质量 通过传感器检测到的数据计算后的值实时刷新到显示器中,主要的功能包括以下几点: ①可以对温度实时检测; ②可以对水质实际值实时检测; ③水质浑浊预警 主要特点: 1.以STM32单片机为核心,配合水质模块; 2.主要完成系统的 功能控制、状态显示、信息检测以及报警硬件组建所单片机和传感器等元器件的选择; 3.完成系统控制的软件设计编程; 4.实现对水质检测、温度检查、预警的功能 内容包含: 1、原理图工程 2、PCB工程 3、源码工程 4、实物照片 5、详细介绍说明书-22531字 6、实物照片 7、浊度传感器资料

    人脸识别_seetaface6_SDK_多功能应用开发工具包_1741771332.zip

    人脸识别项目实战

    华中科技大学计算机科学研究生复试上机测试题.zip

    华中科技大学计算机科学研究生复试上机测试题.zip

    YOLOv8部署到web上(Django+html)

    YOLOv8部署到web上(Django+html)

Global site tag (gtag.js) - Google Analytics