推荐一款开源的Diffy自动化测试框架
1. 前言
软件测试是软件开发生命周期一个十分重要的环节,测试工作开展的好坏,很大程度上决定了产品质量的好坏,但软件产品随着版本的持续迭代,功能日益增多,系统愈加复杂,而从质量保障的角度,除了要保障好每次新增、优化的产品质量外,还需要确认新增或修改的功能不影响之前已存在的功能。若要进行产品功能全量回归,这个测试的工作量将会非常巨大。同时因为是回归,可能几百甚至上千用例中才会发现一个问题,甚至一个问题也没有,测试投入工作的时间与最终的收益不成比例。
因此如何在有限的时间、人力投入下,有效、高效的保证产品回归测试的质量,也一度成为了行业老司机以及团队管理者头疼的问题!

而今天的主角Diffy则为上述问题提供了较好的解决方案。它基于稳定版本和它副本的输出,对候选版本的输出进行严格对比,以检查候选版本是否正确,大大降低了回归工作量。
接下来,让我们详细了解一下Diffy的工作原理,以及结合实战演练带大家感受一下它的魅力。
2. 关于Diffy
关于Diffy,公号此前发表过一篇文章:
推荐一款Diffy:Twitter的开源自动化测试工具
有过详细介绍,之前不了解的读者,可详细阅读一下。
简单来理解,Diffy是一个开源的自动化测试工具,是一种自动Diff测试技术。它能够自动检测基于Apache Thrift或者基于HTTP的服务。通过同时运行新/老代码,对比运行结果,发现潜在bug。并且使用Diffy,只需要进行简单的配置,而不需要再编写测试代码。
3. Diffy工作原理
在整个测试开展过程中,Diffy需要部署三个版本的系统,以实现它的噪声过滤和对比功能,它们分别是:
- 候选版本(candidate):该版本为待测版本,有着最新待测代码。
- 稳定版本(primary):该版本通常是已经上线版本,或者是已知功能正常的版本。
- 稳定版本副本(secondary):该版本是稳定版本的副本,和稳定版本运行相同的代码,主要用于排除噪声。
而Diffy主要职责充当了一个前置代理服务的角色,它能够将来源请求分发到不同版本的系统中去,通过对各个版本系统的输出进行对比,做出最终的结论。
Diffy整个工作原理流程图如下:

说明:
diffy本身作为一个代理服务(proxy),需要人为构造或引流http请求,发到proxy代理服务中。- 当proxy代理服务接收到请求后,会把请求分发到三个地方:被测服务,通常称之为侯选版本(
candidate)、稳定版本(primary)服务、稳定版本副本(secondary)服务; - 接着,侯选版本服务与稳定版本服务的返回结果进行diff,生成原始diff结果(raw differences),即原始区别;
- 其次,稳定版本与稳定版本副本的返回结果进行diff,生成噪声diff差异值结果(non-deterministic noise),即噪声,通过对这些差异值做减法来消除噪声。
- 最后,通过比对原始的diff结果与消除噪声后的结果,得到最终的diff结果通过去噪声,得到最终过滤后的diff结果(filtered differences);
最终过滤后的对比结果会在平台提供的html页面中展示出来。
为了方便大家更好的理解上述工作流程,在网上找了一张图,标注了一下示例(本图来源于网络):

其中:
- 原始区别为候选版本和稳定版本之间输出的区别,其中可能会包含上述的噪声。
- 噪声从稳定版本和其副本中获得,如果两个运行相同代码的系统输入相同输出却不同,则Diffy会认为这是开发人员不需要关心的噪声。
基于上述两个区别集合,Diffy可以识别出候选版本和稳定版本真实的区别,这些区别很有可能就是一个缺陷。
当然,对于一个概率性出现随机值,仅仅一次请求的结论可能是不准确的。例如对于一个50%概率出现true或者false的布尔值,则有50%的概率会出现候选版本和稳定版本的不同,同时又会有50%的概率出现稳定版本和其副本出现不同(即将这个值认定为噪声),最终会有25%的概率认为这是一个缺陷。因为此时稳定版本和其副本值相同,候选版本和稳定版本值不同。因此,Diffy还会聚合原始区别和噪声,当发现二者出现的概率类似的时候,会认定之前识别出来的缺陷属于误报。
4. Diffy编译、部署
Diffy是Twitter使用scala语言开发的项目,并且在GitHub持续更新中,关于diffy的源码,github上对应有两个版本:
1. twitter/diffy:
https://github.com/twitter/diffy
2. opendiffy/diffy:
https://github.com/opendiffy/diffy
按照官方的说明,建议优先使用opendiffy/diffy进行编译部署。
由于我们最终是需要用到diffy编译成功生成的jar包(实际上diffy平台使用的是scala语言),此时运行环境需要安装JDK,这里建议安装Java 8,编译环境安装好之后,克隆diffy源码并进行sbt编译构建。
git clone https://github.com/opendiffy/diffy
cd diffy
./sbt assembly
需要注意的是./sbt assembly这个编译下载过程十分漫长,有条件的同学建议挂个代理。

编译好之后,生成的Jar包位置:diffy/target/scala-xx/diffy-server.jar(diffy根目录的相对路径下)
除了利用Github的源码进行搭建外,还有两种方式也可以搭建Diffy。其一是直接利用jar包,但该方法或者使用docker的Diffy容器(https://hub.docker.com/r/diffy/diffy)进行搭建,在此不一一赘述。
5. Diffy常用命令参数
编译生成好jar包后,直接通过java命令启动diffy服务即可,其中,运行Diffy服务的常用参数如下:
| 参数配置 | 含义 |
|---|---|
| candidate='PC1:8888' | 待上线版本部署地址,即候选版本 |
| master.primary='PC2:8888' | 已上线版本地址1,即稳定版本 |
| master.secondary='PC3: 8888' | 已上线版本地址2,即稳定版本副本 |
| service.protocol='http' | http协议或https |
| serviceName='Test Service' | 服务名称 |
| proxy.port=:9990 | Diffy代理端口,所以请求都应从这个端口访问 |
| admin.port=:9991 | 通过http://PC0:8881/admin可查看请求状况 |
| http.port=:9999 | 查看界面,在这里可以比较差异 |
| responseMode=primary | 代理服务器是否返回结果,默认(empty)无返回,可指定primary返回线上版本,secondary(同线上版本,用于噪音消除),candidate(待测试版本) |
| allowHttpSideEffects=true | Diffy考虑到安全性,POST,PUT,DELETE请求默认忽略,因此该参数为true则表示这三种类型请求仍能正常代理发送 |
| excludeHttpHeadersComparison=false | 是否排除header的差异,不同服务器,cookie,nginx版本可能有所差异,设置为true可以忽略这 |
| notifications.targetEmail | (对差异发送到指定邮箱) |
例如:
java -jar diffy-server.jar \
-candidate='127.0.0.1:80' \
-master.primary='127.0.0.1:81' \
-master.secondary='127.0.0.1:82' \
-service.protocol='http' \
-serviceName='My Diffy Service' \
-proxy.port=:8880 \
-admin.port=:8881 \
-http.port=:8888 \
-allowHttpSideEffects=true \
-excludeHttpHeadersComparison=false \
-notifications.targetEmail=tester@emal.com
6. Diffy项目实战演练
安装和使用Diffy的一般步骤如下:
- 安装Diffy;
- 启动候选服务、稳定服务和稳定服务副本;
- 运行Diffy;
- 发送请求&查看结果;
接下来,通过一则简单的实战项目示例,为大家演示整个diffy的使用过程。
本文示例项目:是基于Django搭建的一套简易型REST API服务。关于如何通过Django来实现REST API服务过程可参考:Python利用Django 构建Rest Api: 快速入门教程
假设按照上述教程,你已经成功的搭建好了REST API服务,项目名为:blog_project,接下来,继续往下操作:
1. 部署primary(稳定版本)
由于本文不区分线上正式环境和测试环境,皆通过本地环境演示。(读者在实际生产&测试环境操作时,除了环境差异外,操作思路皆一样)
将示例项目blog_project代码拷贝一份到其它目录(为了和测试版本区分开来),激活虚拟环境,启动Django服务,端口设置为8001,此服务作为稳定版本服务,命令如下:
source env/bin/activate
cd blog_project
python manage.py runserver 8001
2. 部署secondary(稳定版本副本)
同上一步操作一样,激活虚拟环境,启动Django服务,端口设置为8002,此服务作为稳定版副本服务,命令如下:
source env/bin/activate
cd blog_project
python manage.py runserver 8002
3. 验证primary和secondary(稳定版本服务)
此步非必须,但为了让大家直观能和测试版本的服务区分开来,我们先验证一下,当前稳定版本服务的接口输出信息,比如:
http http://127.0.0.1:8001/api/
输出信息:

从上述输出信息中,我们可以知道访问api/接口时,会输出两条信息,并且每条记录,分别对应有content,id,title,updated_at,create_at几个字段。
接着验证secondary副本服务:
http http://127.0.0.1:8002/api/

可以看出,secondary副本服务和primary稳定版本服务输出结果是一样的。
4. 部署candidate(测试版本)
接下来,我们开始部署测试版本服务,为了和稳定版本服务有所不同,我们在测试版本中,给api接口请求记录中,增加一个data字段。(实际工作中,也经常会面临接口字段的增、删、改)
1、修改blog_api/models.py文件,在原来的数据模型中,增加一个data字段:
from django.db import models
# Create your models here.
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=50)
data = models.CharField(max_length=250,default='--')
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
2、修改serializers.py文件,在fields中增加返回data字段。
class PostSerializer(serializers.ModelSerializer):
class Meta:
fields = ('id', 'title', 'content', 'data','created_at', 'updated_at',)
model = models.Post
3、生成迁移文件、同步执行数据库变更
python manage.py makemigrations
python manage.py migrate
4、启动服务,默认端口为8000,作为待测版本服务。
python manage.py runserver
5. 启动diffy服务
由于演示需要,直接在本地启动diffy服务即可,命令如下:
java -jar diffy-server.jar
-candidate=localhost:8000
-master.primary=localhost:8001
-master.secondary=localhost:8002
-service.protocol=http
serviceName=My-Service
-proxy.port=:8880
-admin.port=:8881
-http.port=:8888
-rootUrl='localhost:8888'
-allowHttpSideEffects=true
从上述启动命令中,可知:
- diffy代理接口为8880,后续测试的所以请求都应从这个端口访问
- 查看请求:通过http://localhost:8881/admin (admin.port)可以查看请求状况
- 查看差异:通过http://localhost:8888 (http.port)比较差异
在命令行中,输入如下命令,运行测试:
http http://127.0.0.1:8880/api/
命令经执行后,经diffy代理转发到稳定版本服务(端口8001)、稳定版本副本服务(端口8002)、测试版本服务(端口8000)中。

访问http://localhost:8888,查看diff请求对比界面,功能说明如下图所示:

通常接口差异主要分为以下几类:
- 每次调用本身返回值就不同,如updatetime(可忽略);
- 测试环境和线上环境数据不一致(可忽略);
- 实时数据接口、动态变化数据(可忽略);
- 软件缺陷或非预期修改。
对于可忽略的差异,可点击按钮忽略。
访问http://localhost:8881/admin,查看diff后台界面,功能说明如下图所示:

连续运行几次测试请求,访问http://localhost:8888,对比请求差异,如图所示。

从上图中,可知,已经成功diffy出在测试版本中,新增了一个data字段。
6. 修改测试版本服务
继续在测试版本服务上面修改以验证diffy的有效性,比如修改api/接口返回的记录内容。
1、访问http://localhost:8000/admin,访问测试版本服务后台,修改其中一条记录,比如:

更新date中的内容,并点击保存。此时需要注意,当点击保存后,此时记录的updated_at字段值会被修改。
2、再次运行diffy代理请求。
http http://127.0.0.1:8880/api/
3、此时再观察http://localhost:8888界面,

可以看到,在diffy界面中,检查出了三个差异:返回的内容长度Content-length、data、updated_at。
当然,实际业务中,Content-length、updated_at这类型的差异可被忽略掉。
通过结合接口返回详情功能,可查看到稳定版本和测试版本返回响应的差异处:

7. 小结
最后,小结几点建议:
- 在使用Diffy时,需要通过Diffy代理服务发送待测请求,虽然我们可以通过postman、curl等工具一个个发送,实践时,可通过Charles工具记录所有线上待测请求,然后利用Charles的Rewrite功能将修改成Diffy的代理服务器地址,重写请求,再重发。
- 除上借助Charles代理工具外,在实际应用时,也可借助线上引流工具(比如通过goreplay等引流工具)进行请求流量回放,或通过已有的接口自动化测试用例触发请求。
- 在使用Diffy时,可以看到有些差异是请求头部导致的,并不是我们想要发现的内容上的差异,如cookie的差异,nginx版本的差别,不同服务器等等,可以在命令行中加入配置可忽略头部差异:
excludeHttpHeadersComparison=true
如果你觉得文章还不错,请转发分享下,你的肯定是我最大的鼓励和支持。
推荐一款开源的Diffy自动化测试框架的更多相关文章
- 【转】推荐10款最热门jQuery UI框架
推荐10款最热门jQuery UI框架 原创 在进行Web开发时,并非所有的库都适合你的项目,但你仍需要收藏一些Web UI设计相关的库或框架,以在你需要的时候,加快你的开发效率.本文为你推荐10款非 ...
- 【转】推荐4个不错的Python自动化测试框架
之前,开发团队接手一个项目并开始开发时,除了项目模块的实际开发之外,他们不得不为这个项目构建一个自动化测试框架.一个测试框架应该具有最佳的测试用例.假设(assumptions).脚本和技术来运行每一 ...
- metasploit 一款开源的渗透测试框架
渗透神器漏洞利用框架metasploit from: https://zhuanlan.zhihu.com/p/30743401 metasploit是一款开源的渗透测试框架软件也是一个逐步发展与成熟 ...
- 推荐25款实用的 HTML5 前端框架和开发工具【下篇】
快速,安全,响应式,互动和美丽,这些优点吸引更多的 Web 开发人员使用 HTML5.HTML5 有许多新的特性功能,允许开发人员和设计师创建应用程序和网站,带给用户桌面应用程序的速度,性能和体验. ...
- 推荐五款Android 应用的自动化测试工具
如今自动化测试已经应用到每天的测试中.这不足为奇,因为自动化测试在测试过程中节约了时间,还能避免包括人为因素造成的测试错误和遗漏. 自动化测试工具选择很多.一些是开源的,一些非常贵.一些自动化工具是几 ...
- 推荐一款开源的C#TCP通讯框架
原来收费的TCP通讯框架开源了,这是一款国外的开源TCP通信框架,使用了一段时间,感觉不错,介绍给大家 框架名称是networkcomms 作者开发了5年多,目前已经停止开发,对于中小型的应用场景,够 ...
- 推荐几款开源的js日期控件
做为一个正规的网站,经常需要一些日期或时间的筛选,所以我们今天就推荐二十多款javascript的js日期/时间筛选插件.个个经典,绝对有你需要的. My97DatePicker ,国人开发的一款js ...
- Galileo:一款开源Web应用审计框架
转载自FreeBuf.COM Galileo是一款针对Web应用程序的开源渗透测试工具,可帮助开发和渗透测试人员识别并利用其Web应用程序中的漏洞. 截图 安装 $ git clone https:/ ...
- 推荐一款 在线+离线数据 同步框架 Dotmim.Sync
移动智能应用可以分为在线模式.纯离线模式与"在线+离线"混合模式.在线模式下系统数据一般存储在服务器端的大中型数据库(如 SQL Server.Oracle.MySQL 等),移动 ...
- 推荐一款开源的原型设计软件--pencil
如果觉得内置的元素不够,可以直接用类似屏幕截图软件直接剪切粘贴,并且可以制作自己的元素集合.很好用 http://pencil.evolus.vn/ Easy GUI Prototyping Penc ...
随机推荐
- Mac | 解决 MacOS 配置 Maven 出现的 Java_Home Error
1. 错误信息 2. 解决方案 2.1 对于Windows系统下解决方案 https://blog.csdn.net/frankarmstrong/article/details/69945774,在 ...
- 深入浅出 ZooKeeper
ZooKeeper 是一个分布式协调服务 ,由 Apache 进行维护. ZooKeeper 可以视为一个高可用的文件系统. ZooKeeper 可以用于发布/订阅.负载均衡.命令服务.分布式协调/通 ...
- BTC-协议
BTC-协议 一个去中心化的数字货币要解决两个问题 1.谁有权发行货币 比特币的发行是由挖矿决定的(coinbase transaction 唯一一个产生新币的途径)比特币通过挖矿来决定货币的发行权, ...
- VirtualBox 设置开机自动在后台启动虚拟机
打开 C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp 新建文件 virtualbox.bat 编写脚本 "C:\Pr ...
- spring--AOP通知类型有哪些
Spring AOP(Aspect-Oriented Programming,面向切面编程)提供了五种类型的通知(advice),这些通知定义了切面(aspect)是在目标对象的方法执行的哪个点被应用 ...
- UEditor 添加在线管理图片删除功能 (转载)
第一,需要添加一个 php 文件来实现删除功能,文件添加到: ueditor\php\action_delete.php 代码内容: <?php /*---------------------- ...
- 给Hexo博客文章加密
有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准https://blog.zysicyj.top 首发博客地址 原文地址 这是个啥 首先, 这是 Hexo 生态圈中 最好的 ...
- 凡是有但是-varchar和nvarchar的初步学习之一
凡是有但是-varchar和nvarchar的初步学习之一 背景 高应用开发, 在涉及到国内国外的问题时,重要的事情有两个: 时区转换, 字符集转换. 时区转换虽然是很难理清楚, 各种规范不统一的事情 ...
- 【转帖】x86服务器中网络性能分析与调优(高并发、大流量网卡调优)
最近在百度云做一些RTC大客户的项目,晚上边缘计算的一台宿主机由于CPU单核耗被打满,最后查到原因是网卡调优没有生效,今天查了一下网卡调优的资料,欢迎大家共同探讨. 一.网卡调优方法 1.Broadc ...
- 【转帖】32.MinorGC、MajorGC和FullGC的对比
目录 1.MinorGC.MajorGC和FullGC的对比 2.GC触发机制 1.MinorGC.MajorGC和FullGC的对比 1.JVM在进行GC的时候,并不是每次都是对新生代.老年代.永久 ...