PowerMock在Java开发自测中的应用

一、背景介绍
Java开发中的单元测试,不仅可以检测代码逻辑的正确性,同样也可以通过边界测试用例考验代码健壮性,它是开发过程中重要的质量保证手段。单元测试用例以及持续集成测试用例不断增加和迭代会驱动代码不断完善。本文以PowerMock工具作为主要的讨论对象,通过开发过程中遇到的不同的问题场景,阐述对应问题场景下PowerMock在单元测试发挥的作用。
所谓Mock对象实际是将类似于数据库操作、调用远程接口等不影响原有代码逻辑但是依赖于外部对象的对象行为进行模拟。在单元测试过程中将Mock出来的对象代替程序实际运行中的对象调用,使其并不真正去执行对象调用,只返回需要的数据即可。通过这样的方式来验证主程序中的逻辑正确与否。
可以在POM文件中进行如下配置来在项目中引入PowerMock辅助我们进行单元测试:

<properties>
    <powermock.version>1.5.6</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>

二、PowerMock实例介绍
1. Mock静态方法
项目开发过程中,经常会将比较通用的工具性质的方法抽象为一个工具类以便使用者进行调用。本小节讨论静态方法的Mock。

如上图代码所示,我们可以使用mockStatic这样的API来对静态方法进行模拟。
2. 方法中的新建对象Mock
在代码中常用注解来进行对象注入,但是实际开发中也会常用新建对象的方式进行对象
调用。那么在单元测试方法中也需要对其进行Mock以便于获取到自己需要的返回值。因为程序中存在根据新建对象所调用方法的返回值来决定不同程序分支的情况,根据Mock出来的对象不同的返回值有助于测试代码覆盖不同情况下的程序分支。
这里写图片描述
先Mock需要新建的类,再使用whenNew 的API来进行模拟。通过返回的Mock对象来代替程序中的新建对象来使用。
3. 私有方法的Mock
单元测试过程中对于私有方法的Mock常常是一件头疼的事情。有的时候我们甚至通过反射的方式来进行私有方法的单元测试。其实PowerMock中提供了私有方法的Mock方法。
这里写图片描述
图3 Mock私有方法一
其中checkExist为TestService类中的私有方法,通过这样的API实现对于私有方法的Mock。verifyPrivate用于检查类中的私有方法是否被调用。
另外一种Mock私有方法如下所示:
这里写图片描述
图4 Mock私有方法二
4. void方法Mock
对于void方法,由于它没有返回值,没有办法根据其返回值判断程序执行的结果。但是只要是方法,它执行过后肯定会产生效果。对代码执行后的效果进行判断也可以达到测试void方法的目的。在PowerMock中可以使用verify这个API来检测该void方法是否被调用。
这里写图片描述
图5代码中的checkExist方法为一个void方法,通过verify来检测该方法是否被调用。
5. 多次thenReturn的使用
在实际开发中会经常遇到同一个对象方法被多次调用,例如EF框架下的commonDao。
在单元测试的时候发现,如果按照commonDao被调用的顺序进行Mock,运行测试方法的时候会报错。具体错误为在第一次Mock该对象进行thenReturn返回一个需要的返回值,在第二次Mock该对象调用相同方法时会出现类型转换的错误。原因是第二次使用的Mock对象还是上一次Mock遗留下来的,所以在返回值的时候会产生类型不一致无法转换的错误。此时可以使用下图代码的方式。
这里写图片描述
图6 多次thenReturn调用
即在同一个mock方法中不断调用thenReturn方法,根据原先的代码逻辑,每次返回不同的查询对象。这样就会避免上述错误的发生。
6. spy的使用
Mock出来的对象其实是一个假的对象,它并不真正去执行对象的行为。那么有些情况
下,不真正执行方法无法检测程序运行效果。下面通过一个实例来进行说明,TestService类中包含一个创建txt文件write的方法。
这里写图片描述
图7 普通测试方法
事实上图7测试代码并没有生成对应的tt.txt文件,所以无法对于程序本身的正确性进行验证。此时我们需要使用spy来进行单元测试,它不仅可以Mock一个对象,还可以Mock对象一些方法的行为。如下代码。
这里写图片描述
图8 spy的使用
此时使用spy来Mock一个对象后,再调用该对象的write方法。它会根据真实的TestService类来调用其对应的方法。
7. 相关注解
PowerMock最常用的注解有两个,一个是@Runwith,另一个是@PreprareForTest。@Runwith注解显示的表明JUnit使用哪个指定的Runner来运行测试用例。当测试用例中出现静态类模拟或者PowerMockito.whenNew方法等需要提前准备的Mock对象时需要加上@PreprareForTest注解,并将对应的类添加进去。
三、总结与建议
1.总结
PowerMock并不是什么新技术,之所以对它的常用方法进行总结,一方面是将自己编写单元测试用例时遇到的问题以及一些常用技巧进行归纳。方便后续遇到同样问题可以快速解决。另一方面无论是单元测试用例还是集成测试用例都是软件质量的重要保证手段。导致软件质量的原因有很多方面,大致为架构设计问题、软件编码问题、产品设计和需求问题以及运行环境问题。其中软件编码问题是可以在单元测试以及集成测试不断迭代以及其驱动的代码重构中趋于0 bug。所以做好单元测试以及集成测试有着重要的质量意义。本文将单元测试过程中经常遇到的问题归纳总结,便于在遇到相同问题时可以快速明确Mock方案。

2.建议
(1)如果被测试方法分支较多,可以在一个方法中将情况进行判断。因为如果使用多个测试方法,在后续被测试方法有逻辑变动的时候,会大大增加测试方法的维护成本。所以可以在一个测试方法中将各个分支的断言加上,减少后续测试代码的维护难度。
(2)单元测试代码应该加上合理的注释,方法名上添加被测方法的用途的简单介绍,断言上最好加上测试的是哪一条程序分支的说明。这样为他人维护单元测试方法提供方便。
(3)尽量设计较为完善的测试用例,特别是包含边界值的测试用例。足够多的测试用例可以将代码可能遇到的各种情况包括其中,以此来检查程序健壮性。不合理的地方甚至可以驱动代码的重构。

猜你喜欢

转载自blog.csdn.net/diamond_tao/article/details/79977572