1、前言
大家好,今天小编向大家介绍一款自动化接口diff平台–diffy。diffy是twitter的开源项目,通过同时运行新/老代码,对比运行结果,发现潜在bug。diffy的原理是作为代理,截取请求并发送至所有运行的服务实例,通过对比响应结果来发现每次迭代代码中可能存在的问题。
2、diff测试
什么是diff测试呢?
diff就是对比,在这里是对代码输出结果的对比,具体来说,就是通过对比相同输入,相同接口,不同代码的测试,对比其结果的差异,从而发现潜在的bug。
为什么要做diff?
①、场景验证:
比如某个接口返回的数据中的”name”字段获取由user数据库表改为mobile_user数据库表,那么从接口角度来讲,通过对比这个接口在新老版本代码的返回结果,就可以知道其字段的基本正确性与差异性。
②、提升回归效率:
就一般的接口测试来说,每次代码迭代,除了对新接口的测试,还包括对老接口的回归。如果通过手工回归,那么随着接口数量的增加,测试人员的工作量将同样地线性增长,且效率将大幅降低。通过diff测试,可以发现相同接口下内部代码逻辑变更对其输出的影响,测试人员只需要对比diff接口的差异之处(或自动对比),从而大幅减少人工作业的工作量。
diff测试的一般方案
①、分别部署新、老代码:其中老代码为线上稳定版本,新代码为新迭代的测试版本
②、构造测试数据:我们可以手工构造测试数据,也可以对线上的数据进行抽样,用于diff测试
③、运行测试:使用测试数据分别在新、老代码中运行,并捕获测试结果
④、结果对比:对比新、老代码,相同接口下的输出,如果出现差异,则可以通过接口反向定位问题
diff测试的问题
在实际diff测试中,你会发现大部分的接口都会有一定差异,原因是这些响应中存在了噪音,噪音可能包括:
-
server响应中生成的时间戳
-
随机生成的数字
-
系统服务间的有条件竞争
这些噪音难以完全清除,根据内部逻辑、接口定义的不同,噪音的类型也难以枚举,那么怎么办么?diffy能够通过一定的方式,清除这类噪音,保证diff结果不被影响。
3、diffy平台
diffy的原理
根据diffy的github文档,diffy可以作为代理,截取请求并发送至所有运行的服务实例,通过对比响应结果来发现每次迭代代码中可能存在的问题。其中,diffy上运行了三类代码实例:
①、线上稳定版本:一个运行线上稳定版本代码的节点
②、线上稳定版本备份:同样运行了线上的稳定版本,用于消除噪音
③、测试版本:待上线的测试版本,用于和线上环境代码进行对比
整体架构如下:
如图所示,diffy能够比较primary(线上稳定版本)和secondary(线上稳定版本备份)的差异值,通过对这些差异值做减法来消除噪声;通过比较candidate(测试版本)和primary(线上稳定版本)得到基本的diff结果;最后通过比对基本的diff结果与消除噪声后的结果,得到最终的diff结果。
diffy部署
1、克隆代码并构建
git clone https://github.com/twitter/diffy.git
cd diffy
./sbt assembly
2、在localhost:9990
部署primary(线上稳定版本)的代码
3、在localhost:9991
部署secondary(线上稳定版本备份)的代码
4、在localhost:9992
部署candidate(测试版本)的代码
5、启动diff服务:
java -jar diffy-server.jar \
-candidate=localhost:9992 \
-master.primary=localhost:9990 \
-master.secondary=localhost:9991 \
-service.protocol=http \
-serviceName=My-Service \
-proxy.port=:8880 \
-admin.port=:8881 \
-http.port=:8888 \
-rootUrl='localhost:8888'
6、对diffy发一些请求
curl localhost:8880/your/application/route?with=queryparams
7、在http://localhost:8888中检查结果
如图所示,我们可以看到每个请求在不同节点上的差异之处,如果点击“Exclude Noise”,则可以消除噪声,看到最终的diff结果。
4、结语
程序的测试可以证明程序有错,但永远无法证明程序无错。
-戴克斯特拉(Dijkstra)
一段程序,对于测试人员,bug永远是存在的,没有发现只是测试手段的不足。希望各位同仁保持对bug的“恐惧”与对技术的好奇与探索。
https://mp.weixin.qq.com/s/RjajIPpCx92b7ddTHnrfbw
给回归测试减负——Twitter Diffy实战介绍
回归测试是老司机们都头疼的问题,Twitter Diffy则为这个问题提供了新视角。它基于稳定版本和它副本的输出,对候选版本的输出进行严格对比,以检查候选版本是否正确,大大降低了回归工作量。让我们一起来了解一下吧!
前言
测试是软件生命周期一个十分重要的环节,但项目在随着版本的逐步迭代,功能日益增多,系统愈加复杂,当前迭代版本增加的功能相对上一版本已存在功能的比例越来越小,而每次我们都需要保证新增或修改的功能不影响上一版本已存在的功能。若要进行全方位回归,这个测试的工作量将会很庞大的。而且可能几百上千用例中才会发现一个甚至是0个问题,测试投入产出不成比例。
而Twitter Diffy则为上述问题提供了较好的解决方案。它通过基于稳定版本和它的副本的输出,对候选版本的输出进行比较,以检查候选版本是否正确。工作原理
Diffy充当一个代理角色,它能够将来源请求分发到不同版本的系统中去,通过对各个版本系统的输出进行对比,做出最终的结论。
Diffy 需要三个版本的系统,以实现它的噪声过滤和对比功能,它们分别是:
-
候选版本:该版本是待测版本,相对于生产环境版本有着跟新的代码;
-
稳定版本:该版本通常是已经上线版本,或者是已知功能正常的版本;
-
稳定版本副本:该版本是稳定版本的副本,和稳定版本运行相同的代码,主要用于排除噪声。
运行流程如下:
其中:
- 原始区别为候选版本和稳定版本之间输出的区别,其中可能会包含上述的噪声;
- 噪声从稳定版本和其副本中获得,如果两个运行相同代码的系统输入相同输出却不同,则 Diffy 会认为这是不需要关心的噪声。
基于上述两个区别集合,Diffy 可以识别出候选版本和稳定版本真实的区别,这些区别很有可能就是一个缺陷。例如,图中所示的例子,原始区别中包含了噪声,通过稳定版本和稳定版本副本的对比,可以进一步过滤噪声。项目实战
简单示例
安装和使用Diffy的一般步骤如下:
-
安装Diffy;
-
启动候选服务、稳定服务和稳定服务副本;
-
运行Diffy;
-
发送请求&查看结果;
我们来看一个简单的示例,来大致了解Diffy的工作过程和使用方式:
在成功安装并运行Diffy后,启动三个简单的本地服务来对比:候选版本(返回“hello world”)、稳定版本(返回“hello!!!”)、稳定版本副本(返回“hello!!!”)。
通过Diffy代理服务发送待测试的请求到三个不同版本后,可以看到Diffy的差异结果如下:
从该示例的差异结果可以看到,共测试了一个请求,且该请求结果不一致,故失败率为100%,查看具体详情,发现不一致的地方为返回结果的具体值和长度不一致。因此,Diffy结果与对比的服务一致。
大麦实战
大麦是一个商品数据运营的平台,我们在测试大麦及其他数据产品的过程中,常需要回归旧版本的功能以保证不被当前版本需求所影响。由于完全回归所有旧版本功能的工作量十分庞大,一般来说会先选择主干功能及在需求分析过程觉得可能会被影响的功能作为回归案例。但是这个过程会有以下几个问题:
- 首先,非常耗时耗力,虽然只回归主干功能和测试分析中觉得可能被影响的功能,但这个回归量仍不容小觑;
- 其次,回归覆盖不全,由于并不是回归所有的功能,仍然存在遗漏缺陷的可能性;
因此,在大麦测试中引入Diffy,将所有线上请求作为待测范围,对比测试环境和线上环境,回归覆盖完全,且大大减少了回归工作量。将Diffy运用到大麦中的主要步骤可参考如下流程图,目前暂时将Diffy部署在本地:
具体步骤如下:1.部署Diffy:
此处先这里将候选版本设置为(test.xxx.com),稳定版本和稳定版本副本均设置为(xxx.com)。在实践过程中发现Diffy可以直接代理到线上环境,而无法代理到测试环境。故实战部署时,在Diffy代理和各版本地址间加了一层代理(暂用charles,后续会需要优化,详见第3章节)。
2.发送请求:在使用Diffy时,需要通过Diffy代理服务发送待测请求,当然我们可以通过postman、curl等工具一个个发送,但笔者觉得太过繁琐,可以优化。因此,实践时,通过Charles工具记录所有线上待测请求,然后利用Charles的Rewrite功能将xxx.com修改成Diffy的代理服务器地址,Cookie修改至最新,再重发。
3.查看Diffy结果
我们可以看到共有1167个请求,有118处差异,这些差异可分为以下几类:
- 每次调用本身返回值就不同,如updatetime(可忽略);
- 测试环境和线上环境数据不一致(可忽略);
- 实时接口(可忽略);
- 软件缺陷;
对于可忽略的差异,可点击按钮忽略。最后发现了两处软件bug,原因都在于测试和线上的配置不同。实战总结
优化内容
在将Diffy引入到项目的过程中,为了更加方便快捷地使用该工具时,在Diffy现有基础上优化了一些内容,上一小节已有简单描述,现总结如下:
- Diffy无法代理到测试环境
在利用Diffy进行对比测试环境和线上环境的差异时,发现Diffy可以直接代理到线上环境,但却无法代理到测试环境。经实践,发现测试环境在使用http协议时会提示只能使用具体的ip+端口号形式,而改用https协议则又提示服务器错误,因此,考虑到Diffy可能内部的机制不适用于测试环境,故目前的方案是在Diffy代理和各版本地址间加了一层Charles代理。Diffy通过代理端口将请求发送至配置中定义的测试环境和线上环境地址,以此来比较不同版本之前的区别。由于现在Diffy无法直接将请求发送至测试环境,因此,解决思路是通过Charles代理转发Diffy请求分别至测试环境和线上环境。此处之所以线上环境也用Charles代理转发的原因是,测试环境和线上环境的协议不同,测试环境为http协议,线上环境为https协议。而Diffy配置时只能配置一种协议,因此,为了统一协议,Diffy配置时采用的是http协议,然后通过Charles转发至线上(https协议)。
- 请求过多
在将Diffy应用到具体项目时,会发现每个项目一般都有众多接口。若每次对比时,都一个个的发送请求,这个成本也较高。因此,可利用工具记录线上的请求,然后重写请求,再重发即可。本人采用的是Charles工具,其他可完成相同功能的工具也可以使用。步骤如下: A. 访问线上环境,记录请求并保存
B. 使用Charles Rewrite功能
设置对于来自于线上的请求,将其host(xxx.com)替换为Diffy代理服务器,且Cookie替换至最新,若有其他替换需求也可在此处自定义规则; C.重发请求后续优化Diffy中间代理方案优化
Diffy无法代理到测试环境域名的问题,目前是通过添加Charles代理的方式解决。但由于每个Charles只能配置一个代理端口,所以不同版本之间使用了不同机器。后续考虑是否能利用其它工具代替Charles,能在同一台机器上设置不同代理,简化配置。另外,也可考虑修改源码的方式,看是否能够解决该问题。
历史请求记录优化
目前针对多个请求的Diffy是采用人工触发请求、工具记录并重发的方式解决,虽然在某种程度上已经较便捷,但是对于很复杂的项目,记录所有请求的成本还是较高。后续考虑是否有其他方案,在无需记录请求的前提下,可自动Diffy。
注意事项
此外,在使用Diffy时还需要注意:
可排除请求头部差异
在使用Diffy时,可以看到有些差异是请求头部导致的,并不是我们想要发现的内容上的差异,如cookie的差异,nginx版本的差别,不同服务器等等,可以在命令行中加入配置可忽略头部差异:excludeHttpHeadersComparison=true稳定版本和候选版本数据不同问题
假设稳定版本和候选版本不是同一份数据,如大麦的自助分析,VIP App的有数页面等。由于数据的不同导致产生了一些不是bug的干扰差异,需要人工排除,会产生一些人力成本。目前我们的有许多项目的测试环境和线上环境数据并不是完全一致的,因此,Diffy更适用于可以灰度发布的项目,可以做到两个版本的接口数据完全一致。实时实时接口使用有成本由于实时接口时序和获取数据链路可能有的些微差异,不同版本之间获取到的实时数据可能会有些许差异,因此也会产生一些不是bug的干扰差异,需要人工排除。线上不能随意操作的接口谨慎使用线上常存在一些不能随意操作的接口,如VIP App中的易信播报,若真实发送该请求到线上,那所有用户均会收到消息,给用户造成不便。无法对重定向的接口DiffyDiffy无法对需要重定向请求进行转发,因此用在重定向的接口上,Diffy的只是原接口的返回值。需要注意的是,上文所述的注意事项提示了一些不太适合Diffy的场景。除了个别几个场景是Diffy完全不适用外,其余均指的是这些接口会产生一些需要一定成本才能排除的干扰差异。总而言之,这些场景下的接口使用Diffy会产生更多的成本,但应结合具体情况,权衡Diffy带来的收益是否值得所需的成本。附录
环境搭建
Diffy是Twitter使用scala语言开发的项目,并且在GitHub持续更新中,可以在Github上下载源码编译 twitter/Diffy ,使用Diffy步骤如下:
1.从Github上克隆Diffy源码:
git clone https://github.com/twitter/Diffy.git
2.构建sbt:
cd Diffy
./sbt assembly
3.启动候选服务、稳定服务、稳定服务副本
4.运行生成的jar包/或含有启动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可以忽略这 |
5.查看结果 可通过设置的Diffy代理端口先发一次请求(如PC0:9990)。 再根据前面运行Diffy命令设定的查看请求和查看页面的端口号,可通过在浏览器输入运行Diffy的ip+设定的端口号查看:
- 查看请求:http://PC0:9990/admin (admin.port)
- 查看差异:http://PC0:9999 (http.port)
除了利用Github的源码进行搭建外,还有两种方式也可以搭建Diffy。
Python接口自动化测试零基础入门到精通(2023最新版)