这是Spock系列的第七篇文章,本篇主要讲解Spock如何扩展第三方power mock对静态方法进行测试

实现原理

前面的文章讲到Spock的单测代码是继承自Specification基类,而Specification又是基于Junit的注解@RunWith()实现的,代码如下:

@RunWith(Sputnik.class)@SuppressWarnings("UnusedDeclaration")public abstract class Specification extends MockingApi {

powermock的PowerMockRunner也是继承自Junit,所以使用powermock的@PowerMockRunnerDelegate()注解可以指定Spock的父类Sputnik去代理运行power mock,这样就可以在Spock里使用powermock去模拟静态方法、final方法、私有方法等

其实Spock自带的GroovyMock可以对groovy文件的静态方法mock,但对Java代码的支持不完整,只能mock当前Java类的静态方法,官方给出的解释如下:

(http://spockframework.org/spock/docs/1.3/all_in_one.html#_mocking_static_methods)

因为我们项目中存在很多调用静态方法的代码,现阶段考虑重构业务代码的成本过高,所以这里使用扩展power mock的方式测试静态方法

Spock 代理 power mock

先看下需要测试的业务代码示例:

public UserVO getUserByIdStatic(int uid){    List users = userDao.getUserInfo();    UserDTO userDTO = users.stream().filter(u -> u.getId() == uid).findFirst().orElse(null);    UserVO userVO = new UserVO();if(null == userDTO){return userVO;    }    userVO.setId(userDTO.getId());    userVO.setName(userDTO.getName());    userVO.setSex(userDTO.getSex());if("上海".equals(userDTO.getProvince())){        userVO.setAbbreviation("沪");        userVO.setPostCode(200000);    }if("北京".equals(userDTO.getProvince())){        userVO.setAbbreviation("京");        userVO.setPostCode(100000);    }if(null != userDTO.getTelephone() && !"".equals(userDTO.getTelephone())){        userVO.setTelephone(userDTO.getTelephone().substring(0,3)+"****"+userDTO.getTelephone().substring(7));    }// 静态方法调用 身份证工具类    Map idMap = IDNumberUtils.getBirAgeSex(userDTO.getIdNo());    userVO.setAge(idMap.get("age")!=null ? Integer.parseInt(idMap.get("age")) : 0);// 静态方法调用 记录日志    LogUtils.info("response user:", userVO.toString());return userVO;}

在倒数第4行和倒数第2行代码分别调用了 "身份证工具类IDNumberUtils.getBirAgeSex()" 和 "LogUtils.info()" 日志记录的方法,如果要对这两个静态方法进行mock,我们可以使用Spock+power mock的方式:

import org.junit.runner.RunWithimport org.mockito.Mockitoimport org.powermock.api.mockito.PowerMockitoimport org.powermock.core.classloader.annotations.PrepareForTestimport org.powermock.core.classloader.annotations.SuppressStaticInitializationForimport org.powermock.modules.junit4.PowerMockRunnerimport org.powermock.modules.junit4.PowerMockRunnerDelegateimport org.spockframework.runtime.Sputnikimport spock.lang.Specification

/** * 测试静态方法mock * @Author: www.javakk.com * @Description: 公众号:Java老K * @Date: Created in 20:53 2020/7/16 * @Modified By: */@RunWith(PowerMockRunner.class)@PowerMockRunnerDelegate(Sputnik.class)@PrepareForTest([LogUtils.class, IDNumberUtils.class])@SuppressStaticInitializationFor(["com.javakk.spock.util.LogUtils"])class UserServiceStaticTest extends Specification {    def processor = new UserService()    def dao = Mock(UserDao)

    void setup() {        processor.userDao = dao        // mock静态类        PowerMockito.mockStatic(LogUtils.class)        PowerMockito.mockStatic(IDNumberUtils.class)    }

    def "GetUserByIdStatic"() {        given: "设置请求参数"        def user1 = new UserDTO(id:1, name:"张三", province: "上海")        def user2 = new UserDTO(id:2, name:"李四", province: "江苏")        def idMap = ["birthday": "1992-09-18", "sex": "男", "age": "28"]

        and: "mock掉接口返回的用户信息"        dao.getUserInfo() >> [user1, user2]

        and: "mock静态方法返回值"        PowerMockito.when(IDNumberUtils.getBirAgeSex(Mockito.any())).thenReturn(idMap)

        when: "调用获取用户信息方法"        def response = processor.getUserByIdStatic(1)

        then: "验证返回结果是否符合预期值"        with(response) {            name == "张三"            abbreviation == "沪"            postCode == 200000            age == 28        }    }}

在UserServiceStaticTest类的头部使用@PowerMockRunnerDelegate(Sputnik.class)注解,交给Spock代理执行,这样既可以使用 Spock + groovy 的各种功能,又可以使用power mock的对静态,final等方法的mock

@SuppressStaticInitializationFor(["com.javakk.spock.util.LogUtils"])

这行代码的作用是限制LogUtils类里的静态代码块初始化,因为LogUtils类在第一次调用时会加载一些本地资源配置,比较耗费时间,所以可以使用power mock禁止初始化

然后在setup()方法里对两个静态类进行mock设置,PowerMockito.mockStatic(LogUtils.class),PowerMockito.mockStatic(IDNumberUtils.class)

最后在GetUserByIdStatic测试方法里对getBirAgeSex()方法指定返回默认值:PowerMockito.when(IDNumberUtils.getBirAgeSex(Mockito.any())).thenReturn(idMap)

(power mock的具体用法网上资料很多,这里不展开说明)

运行时在控制台会输出:

Notifications are not supported for behaviour ALL_TESTINSTANCES_ARE_CREATED_FIRST

这是powermock的警告信息,不影响运行结果

(完整的源码在公众号里回复spock获取)

另外,如果你的单元测试代码不需要对静态方法, final方法mock, 就没必要使用power mock, 使用Spock自带的Mock()就足够了

因为power mock的原理是在编译期通过ASM字节码修改工具修改我们的代码,然后使用自己的classLoader加载,加载的静态方法越多会相应的增加测试时长

下一篇会讲解Spock自带的mock如何和powermock结合使用,从而发挥更强大的作用

- END -


互联网一线java开发老兵,工作10年有余,梦想敲一辈子代码,以梦为码,不负韶华。

扫码关注Java老K,获取更多Java干货。

powermockito测试私有方法_Spock代码讲解 静态方法测试相关推荐

  1. 测试框架 如何测试私有方法_高效的企业测试–测试框架(5/6)

    测试框架 如何测试私有方法 本系列文章的这一部分将介绍测试框架以及我在何时以及是否应用它们方面的想法和经验. 关于测试框架的想法 我对大多数测试框架不太满意的原因是,按照我的观点,它们大多增加了语法上 ...

  2. powermockito测试私有方法_03 增强测试: 静态、私有方法处理

    mockito 已经很强大,能帮我们完成大部分 mock 工作,但是对于一些特殊方法来说,还是无能为力. 例如,当我们使用系统获取当前时间戳的时候,可能会调用 System.currentTimeMi ...

  3. 测试私有方法_史上最轻量!阿里开源了新型单元测试Mock工具

    点击上方蓝色字体,选择"设为星标" 回复"666"获取面试宝典 TestableMock是基于源码和字节码增强的Java单元测试辅助工具,包含以下功能: 访问被 ...

  4. JUnit 3.8 通过反射测试私有方法

    测试私有(private)的方法有两种: 1)把目标类的私有方法(修饰符:private)修改为(public),不推荐,因为修改了源程序不佳 2)通过反射 (推荐) 代码演示: 目标程序 Priva ...

  5. python方法测试怀孕_在Python中测试私有方法(例外)

    在阅读了关于在Python中测试私有方法的内容之后,特别是在How do I unit test the methods in a method object?处引用了接受的答案,看来最好只测试公共接 ...

  6. mockito+junit 单元测试 测试私有方法利用反射去调用提升覆盖率 反射调用时传入参数为 null

    今天公司安排我写单元测试 因为要通过三级认证 公司要求是覆盖率必须达到100% 而在写单元测试的时候 对于是否要测试私有方法一直是一个争议 公司规范 对私有方法也进行测试 代码如下: private ...

  7. junit 测试似有方法_JUnit测试私有方法(protected方法类同)

    注意到,我们在之前被测试的方法都是共有的...其实就算是protected修饰的方法我们也可以通过在同一个包下面建测试类来测试的.但是,现 在我们有一个需求,就是要测试私有的方法....按照我们之前的 ...

  8. Python面向对象—新旧式类,私有方法,类方法,静态方法

    一.私有属性和私有方法 1.私有属性是对象不希望公开的属性,私有方法是对象不希望公开的方法.在定义私有属性和私有方法时,在属性或者方法前,加上__(两个下划线) class Woman():def _ ...

  9. JUnit测试私有方法(protected方法类同)

    注意到,我们在之前被测试的方法都是共有的...其实就算是protected修饰的方法我们也可以通过在同一个包下面建测试类来测试的.但是,现 在我们有一个需求,就是要测试私有的方法....按照我们之前的 ...

  10. html皮肤类型测试,肌肤测试的方法 怎么简单有效的测试皮肤类型

    大家在购买化妆产品和护肤产品之前,一定要弄清楚自己的皮肤是哪一种类型,因为每一个人的皮肤类型不一样,有的人是油性皮肤,有的人是干性皮肤,还有的人是混合性皮肤,只有正确选择护肤品和化妆品,才能够达到好的 ...

最新文章

  1. Python的零基础超详细讲解(第六天)-Python的数字类
  2. Runtime编译环境搭建
  3. python高斯分布训练_Python画图高斯分布的示例
  4. struts2校验再提交多条提示信息
  5. C++实现的队列queue
  6. 《高性能MySQL》读书笔记-mysql数据类型和应用
  7. WPF中如何重新定义Main函数
  8. BadZipfile: File is not a zip file
  9. Kubernetes部分Volume类型介绍及yaml示例
  10. 【Java程序设计】运算符与优先级
  11. GIWAXS数据处理流程详细
  12. 基于linux的嵌入式触摸屏,基于嵌入式linux的触摸屏驱动设计
  13. MyBatis 插件原理与实战
  14. Vue中 keep-alive 详解
  15. chrome浏览器 自带网页截取长图功能
  16. “24岁,一门手艺,年入百万”:真正厉害的人,都做到了这4件事
  17. 网站服务器要有固定IP地址吗,你知道动态IP和静态IP服务器有何区别吗?
  18. excel怎么按颜色统计单元格个数
  19. Python画图教程~海龟画图~turtle
  20. CaptureScreenshot

热门文章

  1. vgextend 扩容卷组,即把物理卷加入卷组
  2. android 对文件的读写,对android的/sys/文件进行读写操作
  3. js休眠5秒_JS 之秒开方案大全
  4. 手机qq2008触屏版_手机版卖家中心在哪里
  5. lora信号测试小助手_LoRa技术——30个常见问题解答
  6. openpyxl安装_Openpyxl玩转Excel(一)——新建、读取、复制
  7. 数组、集合、链表实现学生成绩管理系统
  8. 整型数组 java_java创建一个整型数组,数组的大小由用户输入?
  9. CMU 15-213 Introduction to Computer Systems学习笔记(23) Thread-Level Parallelism
  10. 15款13英寸低配mbp的外接显示器选择