今天分享主要面向java语言的开发和测试开发同学。 先说单元测试、mock测试的定义,再比较几个主流的用于单元测试的mock框架。 说说我遇到的痛点,我在跟踪了公司很多工程的git源码,很少同学进行单元测试,即使做单元测试,java开发童鞋也是使用springjunit,springjunit实际上要启动整个spring框架,相当于基于联通、联调式的单元测试。 根据我的痛点和大家面临的问题,设计了产品思路,然后快速用代码实现了一个mock框架,右面迭代了几个版本,添加了好几个特性。

一、单元测试&mock

1.1 单元测试定义

定义:为检查某个方法是否如预期工作,而写的测试代码。
单元:代码中可度量的最小单元,方法或者函数。
结果:不同的case输入对应的输出是否与预期一致。

图片

1.2 测试层次

如微基准测试、单元测试、功能测试、集成联调测试。如下图,还有一些,我没列出来。

图片

1.3 单元测试的例子


public int sum(int a,int b){
	return a + b;
}
@Test
public void sumTest(){
	int sum = sum(1,2);
	assertTrue(sum==3);
}

例子很简单,现实中很少这样简单的代码,现实中的代码很复杂,相互依赖多。就引发了mock造假数据进行测试。

二、mock测试的简介

定义:用来虚拟制造对象及数据的,被虚拟制造的对象。
作用:辅助单元测试快速进行的主要手段。
原因:被测试的方法里,依赖的对象,需要被快速被虚拟制造。

mock就是俗称的挡板。

图片

mock测试的好处,主要是隔离系统或者模块,快速并行开发测试和演示。

图片

当然单元测试本身也会倒逼你进行代码抽象和简洁。通常进行codereview,从单元测试入手,是比较鸡贼的好方法。

2.1 被测试方法的示例


public List saveStudent(Student student){
    List<Student> studentList = studentDao.query(student);
    。。。。。
    studentDao.save(student);
    }

省略了很多代码(可以见文末的github源码)。这示例里,case做的不太好,篇幅限制。 mock对象:studentDao,mock方法:query、save。 用例case:输入参数student,mock的返回值,示例里没有展示。

图片

2.2 主流mock框架

java用于单元测试的主流mock框架:easymock、mockito、powermock、spock。 这些框架要学习的关键词包括:given、and、when、 then、 thenReturn 、andReturn、 verify、 where、 times、 expect、 replay等…… 底层实现原理:无非是对被mock的类、接口、方法,进行动态代理或者字节码增强;

图片

2.3 Mockito使用示例

左边图片是被测试的方法,里面调用几个外部对象,包括dao、rpc,然后根据返回的数据进行过滤、处理。右边是mockito框架进行mock测试,可以看出用了刚才提到的很多关键词。 这些都是高阶函数,目前jdk8也支持了函数式编程。函数编程用户体验好。

import static org.mockito.Mockito.mock; 
import static org.mockito.Mockito.when; 

可以通过import static 引入让高阶函数达到最简易。

图片

2.4 Spock使用示例

它使用groovy脚本编写。 groovy另一个杀手级应用是gradle,代替xml实现maven经纬度的繁琐配置。 groovy最大程度兼容了java语法,同时也能用自己的胶水语言风格,也能和java所用框架轻易结合起来,比容spring。 左边图不变,是被测试方法内容。 右边是groovy的mock测试,坐过BDD开发的,应该感到很亲切。

image

以上介绍了两款mock测试框架,熟悉java开发的同学,应该看出来了,有些缺点。

2.5 主流mock框架缺点

基于以上问题,我根据痛点出发,琢磨了自己的产品思路。

图片

三、pmock框架介绍

我做的这个产品,取名pmock。主要特点:

如下图的概览。

图片

3.1 pmock代码学习成本

如上图的mockTarget、mockObject、mockField、target四个词。
实现原理:是通过对被mock的类、方法,绑定case脚本文件,进行代理或者代码增强。
如下图:被mock的类和caseConfig目录下的脚本文件一 一映射。可以支持js、groovy、ruby、python等脚本。

图片

3.2 具体使用讲解

图片

  1. PersonBusinessDao.java里有queryPersonList方法,所以需要对PersonBusinessDao的方法queryPersonList进行mock,那么如上图脚本文件里也有。
  2. 上图可以认为就是某个被mock方法的case管理,case无非就是一堆if else,根据输入参数,返回预期想要的数据。
  3. 输入参数由pmock框架对输入对象序列化成json字符串,然后返回的json串有pmock反序列化成对象。当然如果是基本类型,pmock也会自动识别。

另外,如果要返回异常、设置超时、设置响应时间,都可以在脚本的方法里,根据脚本语言特性自己编写。

3.3 PMock特性

详细介绍下pmock优点的几个特性

特性1:兼容springjunit测试,零侵入。即启动spring容器进行单元测试。

图片

下图是正常的springjunit单元测试,实际上PersonBusinessService是被pmock框架进行代理注入的。最终queryStudents方法里面依赖外部调用,都走mock。 不小心创造的好处:单元测试之外的mock。 最后一个特点,可以方便让基于spring框架的微服务之间快速进行各种case的mock测试。不用跨网络,在本机调用就可以。 强调一点:一定要对spring容器启动进行优化,不该注入别注入(可以索要ppt,里面讲如何优化spring容器启动)。

特性2:显式使用pmock方法,侵入性极简。使用了mockTarget、mockObject、mockField、target四个词进行mock制作。

图片

特性3:case在线集中配置,让case配置彻底脱离工程,这和配置中心一个原理。

image

右图和左图在一个页面。右图可以在线运行你左图配置的case,在线输入参数,然后返回你预期的数值。 pmock框架启动,如果不走本地工程case映射,将向case中心请求case文件。源码工程pmockserve需要单独部署服务器,用于case配置中心。

特性4:也是最早的版本,使用探针javaagent无侵入

下图红框里即是通过探针进行代码增强的。现在不鼓励使用这个特性,容易忘记配置javaagent,且不能实现接口mock。

图片

四、延伸

单元测试的倒逼代码设计好处。在开发代码时,要考虑如果单元测试;在写单元测试,也考虑怎么方便codeview。 如果被测试的方法和类,不够简洁,太复杂,依赖的对象太多。是对mock测试最大的伤害,因为需要依赖3个以上的mock对象,再方便的case管理,也很难维护内部的逻辑了。 简单来说,如果一个人没有做过单元测试,最好不要和他谈TDD、BDD。善于单元测试的人,可以快速接受TDD。

image

springjunit启动优化,可以索要ppt进行查看。github源码地址:https://github.com/yangtaihsou/pmock

尾声:如果您觉得我产品思路不好,可以指摘。如果觉得好,可以帮忙推荐,试用下,可以联系微信号sinpina进行咨询。


Q&A

Q:因为金融产品业务复杂,如果持续添加case,会太麻烦;
A:金融case复杂,更要单元测试。且最好优化方法,每个方法最好做到依赖mock对象少一点;单元测试是个很好的倒逼代码抽象、简洁的方式,所以才有TDD开发;
Q:mock和挡板有什么区别?
A:mock就是挡板;
Q:接口类的mock,都需要-javaagent:realpath\pmock-agent.jar这种方式配置吗?
A:不是。探针javaagent只是pmock的一种方式,可以完全不使用,直接用显示的声明;

mockTarget(PersonBusinessServiceImpl.class). mockObject(PersonBusinessDao.class). mockObject(PlayRpc.class). mockField(&quot;playRpc&quot;). mockField(&quot;personBusinessDao&quot;).target();

探针javaagent,不能mock接口,且需要配置;

Q:arget这个方法的作用是什么?
A:作用是返回被测试的类;前面需要将mock的对象,赋给被测试的类,mockTarget(PersonBusinessServiceImpl.class)这是初始化被测试的类,几个关键词,都用了高阶函数风格;
Q:pmock-server里,jackson-all这个jar包哪来的,还有agent里javassist没有,但是org.javassist有
A:Pom里的,我依赖的仓库是我司的。
Q:hamcrest-all 有1.3,没有1.2网上没找到
A:使用1.3应该没问题;


本文档来自支付产品技术交流群的聊天记录整理,由志愿者整理并发布到本网站。如需要及时收到来自支付产品技术交流群的最新消息,请扫码关注“凤凰牌老熊”的微信公众号。新建的支付产品技术群还有不少空位, 本群面向支付行业的有经验(2年以上)的产品经理、软件工程师、架构师等,提供交流平台。如想加入本群,请在本文评论中留言(不公开),说明所在的公司、负责的工作、入群分享的主题和时间。