测试利器 | 一款开源的Diffy自动化测试框架:超详细实战教程讲解

简介: 测试利器 | 一款开源的Diffy自动化测试框架:超详细实战教程讲解

1. 前言


软件测试是软件开发生命周期一个十分重要的环节,测试工作开展的好坏,很大程度上决定了产品质量的好坏,但软件产品随着版本的持续迭代,功能日益增多,系统愈加复杂,而从质量保障的角度,除了要保障好每次新增、优化的产品质量外,还需要确认新增或修改的功能不影响之前已存在的功能。若要进行产品功能全量回归,这个测试的工作量将会非常巨大。同时因为是回归,可能几百甚至上千用例中才会发现一个问题,甚至一个问题也没有,测试投入工作的时间与最终的收益不成比例。

因此如何在有限的时间、人力投入下,有效、高效的保证产品回归测试的质量,也一度成为了行业老司机以及团队管理者头疼的问题!

微信图片_20220524161220.png

而今天的主角Diffy则为上述问题提供了较好的解决方案。它基于稳定版本和它副本的输出,对候选版本的输出进行严格对比,以检查候选版本是否正确,大大降低了回归工作量

接下来,让我们详细了解一下Diffy的工作原理,以及结合实战演练带大家感受一下它的魅力。

2. 关于Diffy


关于Diffy,公号此前发表过一篇文章: 推荐一款Diffy:Twitter的开源自动化测试工具 有过详细介绍,如果之前还不了解的读者,可详细阅读一下。

简单来理解,Diffy是一个开源的自动化测试工具,是一种自动Diff测试技术。它能够自动检测基于Apache Thrift或者基于HTTP的服务。通过同时运行新/老代码,对比运行结果,发现潜在bug。并且使用Diffy,只需要进行简单的配置,而不需要再编写测试代码。

3. Diffy工作原理


在整个测试开展过程中,Diffy需要部署三个版本的系统,以实现它的噪声过滤和对比功能,它们分别是:

  • 候选版本(candidate):该版本为待测版本,有着最新待测代码。
  • 稳定版本(primary):该版本通常是已经上线版本,或者是已知功能正常的版本。
  • 稳定版本副本(secondary):该版本是稳定版本的副本,和稳定版本运行相同的代码,主要用于排除噪声。

Diffy主要职责充当了一个前置代理服务的角色,它能够将来源请求分发到不同版本的系统中去,通过对各个版本系统的输出进行对比,做出最终的结论。

Diffy整个工作原理流程图如下:微信图片_20220524161303.png

说明:

  • diffy本身作为一个代理服务(proxy),需要人为构造或引流http请求,发到proxy代理服务中。
  • 当proxy代理服务接收到请求后,会把请求分发到三个地方:被测服务,通常称之为侯选版本(candidate)、稳定版本(primary)服务、稳定版本副本(secondary)服务;
  • 接着,侯选版本服务与稳定版本服务的返回结果进行diff,生成原始diff结果(raw differences),即原始区别;
  • 其次,稳定版本与稳定版本副本的返回结果进行diff,生成噪声diff差异值结果(non-deterministic noise),即噪声,通过对这些差异值做减法来消除噪声。
  • 最后,通过比对原始的diff结果与消除噪声后的结果,得到最终的diff结果通过去噪声,得到最终过滤后的diff结果(filtered differences);

最终过滤后的对比结果会在平台提供的html页面中展示出来。

为了方便大家更好的理解上述工作流程,在网上找了一张图,标注了一下示例(本图来源于网络):

微信图片_20220524161331.png

其中:

  • 原始区别为候选版本和稳定版本之间输出的区别,其中可能会包含上述的噪声。
  • 噪声从稳定版本和其副本中获得,如果两个运行相同代码的系统输入相同输出却不同,则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这个编译下载过程十分漫长,有条件的同学建议挂个代理。

微信图片_20220524161420.png

编译好之后,生成的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/

输出信息:微信图片_20220524161458.png

从上述输出信息中,我们可以知道访问api/接口时,会输出两条信息,并且每条记录,分别对应有content,id,title,updated_at,create_at几个字段。

接着验证secondary副本服务:

http http://127.0.0.1:8002/api/

微信图片_20220524161540.png

可以看出,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)中。

微信图片_20220524161621.png

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

通常接口差异主要分为以下几类:

  • 每次调用本身返回值就不同,如updatetime(可忽略);
  • 测试环境和线上环境数据不一致(可忽略);
  • 实时数据接口、动态变化数据(可忽略);
  • 软件缺陷或非预期修改。

对于可忽略的差异,可点击按钮忽略。

访问http://localhost:8881/admin,查看diff后台界面,功能说明如下图所示:微信图片_20220524161713.png

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

从上图中,可知,已经成功diffy出在测试版本中,新增了一个data字段。

6. 修改测试版本服务

继续在测试版本服务上面修改以验证diffy的有效性,比如修改api/接口返回的记录内容。

1、访问http://localhost:8000/admin,访问测试版本服务后台,修改其中一条记录,比如:微信图片_20220524161810.png

更新date中的内容,并点击保存。此时需要注意,当点击保存后,此时记录的updated_at字段值会被修改。

2、再次运行diffy代理请求。

http http://127.0.0.1:8880/api/

3、此时再观察http://localhost:8888界面,微信图片_20220524161916.png可以看到,在diffy界面中,检查出了三个差异:返回的内容长度Content-lengthdataupdated_at

当然,实际业务中,Content-lengthupdated_at这类型的差异可被忽略掉。

通过结合接口返回详情功能,可查看到稳定版本和测试版本返回响应的差异处:微信图片_20220524161944.png

7. 小结


最后,总结几点建议:

  1. 在使用Diffy时,需要通过Diffy代理服务发送待测请求,虽然我们可以通过postman、curl等工具一个个发送,实践时,可通过Charles工具记录所有线上待测请求,然后利用Charles的Rewrite功能将修改成Diffy的代理服务器地址,重写请求,再重发。
  2. 除上借助Charles代理工具外,在实际应用时,也可借助线上引流工具(比如通过goreplay等引流工具)进行请求流量回放,或通过已有的接口自动化测试用例触发请求。
  3. 在使用Diffy时,可以看到有些差异是请求头部导致的,并不是我们想要发现的内容上的差异,如cookie的差异,nginx版本的差别,不同服务器等等,可以在命令行中加入配置可忽略头部差异:excludeHttpHeadersComparison=true


目录
相关文章
|
19小时前
|
前端开发 测试技术 Python
《手把手教你》系列技巧篇(四十一)-java+ selenium自动化测试 - 处理iframe -上篇(详解教程)
【5月更文挑战第5天】本文介绍了HTML中的`iframe`标签,它用于在网页中嵌套其他网页。`iframe`常用于加载外部内容或网站的某个部分,以实现页面美观。文章还讲述了使用Selenium自动化测试时如何处理`iframe`,通过`switchTo().frame()`方法进入`iframe`,完成相应操作,然后使用`switchTo().defaultContent()`返回主窗口。此外,文章提供了一个包含`iframe`的HTML代码示例,并给出了一个简单的自动化测试代码实战,演示了如何在`iframe`中输入文本。
10 3
|
1天前
|
JavaScript 前端开发 Java
《手把手教你》系列技巧篇(四十)-java+ selenium自动化测试-JavaScript的调用执行-下篇(详解教程)
【5月更文挑战第4天】本文介绍了如何使用JavaScriptExecutor在自动化测试中实现元素高亮显示。通过创建并执行JS代码,可以改变元素的样式,例如设置背景色和边框,以突出显示被操作的元素。文中提供了一个Java示例,展示了如何在Selenium中使用此方法,并附有代码截图和运行效果展示。该技术有助于跟踪和理解测试过程中的元素交互。
7 0
|
2天前
|
Web App开发 JavaScript 前端开发
《手把手教你》系列技巧篇(三十九)-java+ selenium自动化测试-JavaScript的调用执行-上篇(详解教程)
【5月更文挑战第3天】本文介绍了如何在Web自动化测试中使用JavaScript执行器(JavascriptExecutor)来完成Selenium API无法处理的任务。首先,需要将WebDriver转换为JavascriptExecutor对象,然后通过executeScript方法执行JavaScript代码。示例用法包括设置JS代码字符串并调用executeScript。文章提供了两个实战场景:一是当时间插件限制输入时,用JS去除元素的readonly属性;二是处理需滚动才能显示的元素,利用JS滚动页面。还给出了一个滚动到底部的代码示例,并提供了详细步骤和解释。
26 10
|
2天前
|
Java 测试技术 持续交付
自动化测试框架选型与实战:深入探索与应用
【5月更文挑战第8天】本文探讨了自动化测试框架的选型与实战应用,强调了其在软件质量保障中的重要性。选型原则包括考虑项目需求、技术栈、可扩展性和可维护性,以及社区支持和文档。介绍了Selenium、Appium、JUnit和Pytest等常用框架,并概述了实战应用的步骤,包括明确需求、搭建环境、编写测试用例、执行测试、分析结果、维护代码和持续集成。合理选型与实践能提升测试效率,保障项目成功。
|
3天前
|
JavaScript 前端开发 测试技术
《手把手教你》系列技巧篇(三十八)-java+ selenium自动化测试-日历时间控件-下篇(详解教程)
【5月更文挑战第2天】在自动化测试过程中,经常会遇到处理日期控件的点击问题。宏哥之前分享过一种方法,但如果输入框是`readonly`属性,这种方法就无法奏效了。不过,通过修改元素属性,依然可以实现自动化填写日期。首先,定位到日期输入框并移除`readonly`属性,然后使用`sendKeys`方法输入日期。这样,即使输入框设置了`readonly`,也能成功处理日期控件。
23 1
|
3天前
|
前端开发 测试技术
前端自动化测试中的快照测试原理
快照测试用于前端自动化测试,通过比较当前应用状态与预存预期快照来检测UI变化。流程包括设置测试环境、捕获屏幕快照、保存预期快照、比较快照及处理差异。当快照比较出现差异时,测试工程师审查判断是否为预期变化或错误,确保应用一致性。这种方法在重构、样式更改和跨浏览器测试时提供有效回归测试,减少手动验证工作。
|
3天前
|
数据采集 监控 前端开发
前端自动化测试
前端自动化测试通过脚本和工具提升开发效率,确保应用在不同环境的品质和一致性。关键方面包括单元测试(如Jest、Mocha)、集成测试(Selenium、Puppeteer)、UI测试、快照测试及持续集成工具(Jenkins、Travis CI)。遵循确定测试范围、编写可维护代码、频繁运行测试和监控结果的最佳实践,可增强代码质量,减少错误。
|
4天前
|
Java 测试技术 Python
《手把手教你》系列技巧篇(三十七)-java+ selenium自动化测试-日历时间控件-上篇(详解教程)
【5月更文挑战第1天】该文介绍了使用Selenium自动化测试网页日历控件的方法。首先,文章提到在某些Web应用中,日历控件常用于选择日期并筛选数据。接着,它提供了两个实现思路:一是将日历视为文本输入框,直接输入日期;二是模拟用户交互,逐步选择日期。文中给出了JQueryUI网站的一个示例,并展示了对应的Java代码实现,包括点击日历、选择日期等操作。
19 0
|
5天前
|
敏捷开发 测试技术 持续交付
深入理解自动化测试:框架与实践
【5月更文挑战第5天】 在现代软件开发周期中,自动化测试已成为确保产品质量和加速交付过程的关键环节。本文将深入探讨自动化测试的核心概念、框架选择以及实际实施过程中的最佳实践。通过分析各种自动化测试工具和技术的优缺点,我们旨在为读者提供一种系统化的方法来构建和维护有效的自动化测试环境。
|
5天前
|
机器学习/深度学习 人工智能 自然语言处理
自动化测试中AI驱动的决策框架设计与实现
【5月更文挑战第5天】 在软件测试领域,自动化测试已成为提升测试效率和质量的关键手段。然而,随着软件系统的复杂性增加,传统的自动化测试方法面临挑战,尤其在测试用例的生成、执行及结果分析等方面。本文提出一种基于人工智能(AI)的自动化测试决策框架,旨在通过智能化的算法优化测试过程,并提高异常检测的准确率。该框架结合机器学习和深度学习技术,能够自学习历史测试数据,预测高风险变更区域,自动生成针对性强的测试用例,并在测试执行过程中实时调整测试策略。此外,通过自然语言处理(NLP)技术,该框架还能对测试结果进行语义分析,进一步提供更深入的洞察。本研究不仅增强了自动化测试工具的智能性,也为软件质量保证提