关于测试领域的自动化,已有不少的文章作过介绍,“黑科技”也比比皆是,如经过Java字节码技术实现接口的录制,Fiddler录制内容转Python脚本,App中的插桩调试等,可见角度不一样,对最佳实践的理解也不同。这里想要阐述的是,外卖(上海)QA团队应用相对“小众”的Ruby,在资源有限的条件下实现自动化测试的一些实践与经验分享。前端
加入外卖上海团队时,共2名QA同窗,分别负责App与M站的功能测试,自动化测试停留在学习北京侧接口测试框架的阶段,实效上近乎为0,能力结构上在代码这部分是明显薄弱的。而摆在面前的问题是,回归测试的工做量较大,特别是M站渠道众多(4个渠道),移动端API的接口测试需区分多个版本,自动化测试的开展势在必行。在这样的条件下,如何快速且有效地搭建并推广自动化测试体系?在过去对自动化测试的多种尝试及实践的总结后,选择了Ruby。node
简单点说就是:并不聪明的大脑加上“好逸恶劳”的思想,促使我在这些年的自动化测试实践中,不断寻找更合适的解决方案。所谓技术,其本质都是站在别人的肩膀上,肩膀的高度也决定了实现目标的快慢,而Ruby正符合所需的一些特征:mysql
脱离了开发语言的平台,但在不关注白盒测试的状况下并没有太多不妥。当Ruby用于测试开发,基本“屏蔽”了性能上的劣势,充分展示了敏捷、易用的特色,也是选择这一技术路线的主要因素。jquery
接口自动化测试方案众多,我的认为它们都有本身的适用的范围和优缺点。UI类工具虽轻松实现无码Case,但在处理接口变更和全链路接口流程上多少会显得有些繁琐(尤为在支持数据驱动需求下),过多的规则、变量设置和编码也相差无几;录制类型的方案,更多仍是适合回归,对于较全面的接口测试也须要必定的开发量。基于这些权衡考虑,采用一种编码尽量少、应用面更广的接口自动化框架实现方式,把它命名为Coral-API,主要有如下特色:android
测试数据处理独立程序员
学习成本低web
扩展性ajax
以单个接口测试编写为例,下图描述了具体流程:算法
从图中能够看到,安装了Coral-API的gem后,可经过命令行 “coral g {apiname}” ,经过模板来生成测试数据XLS及对应的数据处理文件(例如ApiOne.rb文件),修改并执行ApiOne.rb文件,则能够生成最终的测试数据(YML文件)及测试类和Case文件。若是开发框架支持(有途径可解析出参数),则能够经过脚本直接生成整个服务下全部接口的测试代码,实现自动化Case的同步开发。这种处理过程主要是一并解决了如下几个问题:sql
假设有如下这样一个接口请求格式,包含一个orderInfo的子节点,及payInfo的list,还须要解决一些变化值的问题,如各类id和time(暂且称为动态字段)。通常框架中会以JSON格式来做为测试用例的请求格式,在代码中按变量处理动态字段值。JSON做为请求数据的保存形式,存在一个很大的问题,就是后期维护,尤为是Case数量较多的时候。所以,考虑仍以Excel为数据维护的初始形式(使用上更直观),经过Sheet的嵌套来处理复杂结构,也便于后期接口参数变更后的Case维护。
userId: E000001
requestId: '1938670097'
orderInfo:
orderId: '6778043386'
count: '2'
name: testgoods
payInfo:
- transactionId: '510455433082284'
payTime: '2017-04-04 13:03:34'
payType: BOC
- transactionId: '167338836018587'
payTime: '2017-04-04 13:03:34'
payType: Wallet
createTime: '2017-04-04 13:03:34'
复制代码
测试数据的Excel作以下设计,Main中为第一层参数结构,预期响应另分一个Sheet,子节点和list节点的内容写在对应的Sheet中,动态值均置为空,在接口数据类中处理,orderInfo节点和payInfo节点均另写在新的Sheet中,用于单接口数据驱动的Case与链路回归用Case分开,固然这会增长一些Case维护的成本,能够选择是否区分。
示例的数据结构,经过如下语句便可实现,若是须要为后续接口测试提供前置步骤的数据,也能够同步实现,下例中为后续接口生成了5条请求数据。针对接口参数变更的状况,能够修改Excel和数据处理类文件,执行一遍便可,也提供了批量从新生成全部接口数据的脚本。
class Demo < ApiCaseBase
update self.request,:requestId=>'gen_randcode(10)',:createTime=>'get_datetime'
add_node self.request,"orderInfo",:orderId=>'gen_randcode(10)'
add_list self.request,"payInfo",:transactionId=>'gen_randcode(15)',:payTime=>'get_datetime'
sheetData={'ForApiOther'=>5}
generate_data self,sheetData do
update_force @data,:orderId=>'gen_randcode(10)',:createTime=>'get_datetime'
add_node_force @data,"orderInfo",:orderId=>'gen_randcode(10)'
add_list_force @data,"payInfo",:transactionId=>'gen_randcode(15)',:payTime=>'get_datetime'
end
end
复制代码
Excel做为Case的维护形式,缺点是Case较多状况下频繁读取比较影响时间。在这种状况下,考虑到把数据序列化到YML中,启动执行时接口测试类自动与测试数据进行绑定。在Case中能够直接使用形如 DemoTest.request[1]的请求数据,提升了速度,结构上也清晰了很多。
接口测试类文件(HTTP接口调用为例)生成的模板以下,修改对应的接口信息便可,支持DB验证(代码块p这部分是目前惟一须要写Ruby代码的地方,固然这是非必需项)。
require 'apicasebase'
class PreviewTest
include ApiTestBase
set_cookie
set_domain "Domain_takeaway"
set_port 80
set_path "/waimai/ajax/wxwallet/Preview"
set_method "get"
set_sql "select * from table"
p = proc do |dbres|
# do something
# return a hash
end
set_p p
end
复制代码
TestCase文件以下,原则上无需修改,只须要在测试数据的Excel中编写匹配规则及预期输出,基本上实现了单个接口无编码的数据驱动测试。
require 'Preview_validate'
RSpec.shared_examples "Preview Example" do |key,requestData,expData|
it 'CaseNo'+ key.to_s + ': '+expData['memo'] do
response = PreviewTest.response_of(key)
expect(response).to eval("#{expData['matcher']} '#{expData['expection']}'")
end
end
RSpec.describe "Preview接口测试",:project=>'api_m_auto',:author=>'Neil' do
PreviewTest.request.each{|key,parameter|include_examples "Preview Example",key,PreviewTest.request[key],PreviewTest.expect[key]}
end
复制代码
接口流程Case编写就是各独立接口的业务逻辑串联,重点是Case的组织,把一些公用的Steps独立出shared_examples,在主流程的Case中include这些shared_examples便可,关联的上下游参数 经过全局变量来传递。
RSpec.describe "业务流程测试" ,:project=>'api_m_auto',:author =>'Neil' do
let(:wm_b_client) { WmBClient.new('自配') }
before(:context) do
init_step
end
context "在线支付->商家接单->确认收货->评价" do
include_examples "OrderAndPay Example",1
include_examples "AcceptOrder Example"
include_examples "CommentStep Example"
end
end
复制代码
经过上面的介绍,能够看到,Case的编写大部分能够经过代码生成实现(熟悉之后部分接口也能够根据须要进行操做步骤的取舍,如直接编写YML)。实践下来的状况是,从各方面一无全部,17我的日左右的时间,完成了M站API层接口自动化(业务流程9个,单个接口10个)及点评外卖移动端API的接口自动化(业务流程9个,单个接口20个),实现了外卖业务全链路接口回归,平均每一个业务流Case步骤9个左右。期间也培养了一名以前未接触过Ruby的同窗,在完成了初版开发后,两名初级阶段的同窗逐步承担起了框架的改进工做,实现了更多有效的验证Matcher,并支持了移动端API多版本的测试。以后的回归测试不只时间上缩减了50%以上,也经过接口自动化3次发现了问题,其中一次API不一样版本致使的Bug充分体现了自动化测试的效率。经过ci_reporter,能够方便地将Rspec的报告格式转为JUnit的XML格式,在Jenkins中作对应的展现。
移动端API自动化中存在的问题就是,一个接口会存在多个版本并存的状况,有header中内容不一样的,或formdata内容不一样的状况,在接口回归中必须都要照顾到,在Coral-API中咱们采用如下方式进行处理。
在config.yml中定义各版本的header。
Domain_takeaway_header:
v926: '{"connection":"upgrade","x-forwarded-for":"172.24.121.32, 203.76.219.234","mkunionid":"-113876624192351423","pragma-apptype":"com.dianping.ba.dpscope","mktunneltype":"tcp","pragma-dpid":"-113876624192351423","pragma-token":"e7c10bf505535bfddeba94f5c050550adbd9855686816f58f0b5ca08eed6acc6","user-agent":"MApi 1.1 (dpscope 9.4.0 appstore; iPhone 10.0.1 iPhone9,1; a0d0)","pragma-device":"598f7d44120d0bf9eb7cf1d9774d3ac43faed266","pragma-os":"MApi 1.1 (dpscope 9.2.6 appstore; iPhone 10.0.1 iPhone9,1; a0d0)","mkscheme":"https","x-forwarded-for-port":"60779","X-CAT-TRACE-MODE":"true","network-type":"wifi","x-real-ip":"203.76.219.234","pragma-newtoken":"e7c10bf505535bfddeba94f5c050550adbd9855686816f58f0b5ca08eed6acc6","pragma-appid":"351091731","mkoriginhost":"mobile.dianping.com","pragma-unionid":"91d9c0e21aca4170bf97ab897e5151ae0000000000040786871"}'
v930: '{"connection":"upgrade","x-forwarded-for":"172.24.121.32, 203.76.219.234","mkunionid":"-113876624192351423","pragma-apptype":"com.dianping.ba.dpscope","mktunneltype":"tcp","pragma-dpid":"-113876624192351423","pragma-token":"e7c10bf505535bfddeba94f5c050550adbd9855686816f58f0b5ca08eed6acc6","user-agent":"MApi 1.1 (dpscope 9.4.0 appstore; iPhone 10.0.1 iPhone9,1; a0d0)","pragma-device":"598f7d44120d0bf9eb7cf1d9774d3ac43faed266","pragma-os":"MApi 1.1 (dpscope 9.3.0 appstore; iPhone 10.0.1 iPhone9,1; a0d0)","mkscheme":"https","x-forwarded-for-port":"60779","X-CAT-TRACE-MODE":"true","network-type":"wifi","x-real-ip":"203.76.219.234","pragma-newtoken":"e7c10bf505535bfddeba94f5c050550adbd9855686816f58f0b5ca08eed6acc6","pragma-appid":"351091731","mkoriginhost":"mobile.dianping.com","pragma-unionid":"91d9c0e21aca4170bf97ab897e5151ae0000000000040786871"}'
......
复制代码
在接口测试类被加载时会进行全局变量赋值,同时替换header里对应节点的token,测试数据YML文件中则作这样的描述,每条数据的header则较方便地被替换。
---
Main:
1: &DEFAULT
headers: '<%= $v926 %>'
host: mobile.51ping.com
port: '80'
path: "/deliveryaddresslist.ta"
search: "?geotype=2&actuallat=31.217329&actuallng=121.415603&initiallat=31.22167778439444&initiallng=121.42671951083571"
method: GET
query: '{"geotype":"2","actuallat":"31.217329","actuallng":"121.415603","initiallat":"31.22167778439444","initiallng":"121.42671951083571"}'
formData: "{}"
scheme: 'http:'
2:
<<: *DEFAULT
headers: '<%= $v930 %>'
3:
<<: *DEFAULT
headers: '<%= $v940 %>'
4:
<<: *DEFAULT
headers: '<%= $v950 %>'
5:
<<: *DEFAULT
headers: '<%= $v990 %>'
复制代码
HTTP接口的测试框架选择面仍是比较多的,RPC调用的框架如何测试呢?答案就是JRuby + Java的反射调用,在Pigeon接口中咱们已经试点了这种方式,证实是可行的,针对不一样的RPC框架实现不一样的Adapter(Jar文件),Coral-API传参(JSON格式)给Adapter,Adapter经过解析参数进行反射调用,这样对于框架来讲无需改动,只需对部分文件模板稍做调整,也无需在Ruby中混写Java代码,实现了最少的代码量—2行。
App的UI自动化,Ruby的简便性更明显,尤为Appium提供了对Ruby良好的支持,各类UI框架的优劣就不在此赘述了。综合比较了Appium与Calabash后,选择了前者,测试框架选用了更适合业务流描述的Cucumber,沿用了之前在Web自动化中使用的对象库概念,将页面元素存储在CSV中,包括了Android与iOS的页面对象描述,知足不一样系统平台的测试须要。在针对微信M站的UI自动化方案中,还需解决微信WebView的切换,及多窗口的切换问题,appium_lib都提供了较好的支持,下面介绍下结合了Appium及Cucumber的自动化框架Coral-APP。
框架结构以下图:
step_definitions目录下为步骤实现,public_step.rb定义了一些公共步骤,好比微信测试须要用到的上下文切换,Webview里的页面切换功能,也能够经过support目录下的global_method.rb里新增的Kernel中的方法来实现。
support/native目录下为app测试的配置文件,support/web目录下为h5测试的配置文件。 support/env.rb 为启动文件,主要步骤以下:
$caps = Appium.load_appium_txt file: File.expand_path('../app/appium.txt', __FILE__), verbose: true
$caps[:caps].store("chromeOptions",{"androidProcess":"com.tencent.mm:tools"})
$driver = Appium::Driver.new($caps,true)
Elements.generate_all_objects
Before{$driver.start_driver}
After{$driver.quit_driver}
复制代码
support/elements下为对象库CSV文件,内容以下图:
support/elements.rb为对象库实现,将CSV中的描述转换为Elements模块中对象的功能,这样在Page中就能够直接使用相似“Elements.微信我” 这样的对象描述了。
......
def self.define_ui_object(element)
case $caps[:caps][:platformName].downcase
when "android"
idempotently_define_singleton_method(element["OBJNAME"]){$driver.find_element(:"#{element["ATTRIBUTE"]}","#{element["ANDROID_IDENTITY"]}")}
else
idempotently_define_singleton_method(element["OBJNAME"]){$driver.find_element(:"#{element["ATTRIBUTE"]}","#{element["IOS_IDENTITY"]}")}
end
end
......
复制代码
support/pages为Page层,实现了每一个页面下的操做,目前把它实现为Kernel中的方法,采用中文命名,便于阅读使用。
module Kernel
def 点击我
Elements.微信我.click
end
def 点击收藏按钮
Elements.微信收藏.click
end
def 点击收藏项
Elements.微信收藏连接.click
end
def 点击收藏中的美团外卖连接
Elements.微信收藏连接URL.click
end
end
复制代码
step里的步骤咱们能够这样写,封装好足够的公共步骤或方法,Case的编写就是这么简单。
When /^进入美团外卖M站首页$/ do
点击我
点击收藏按钮
点击收藏项
点击收藏中的美团外卖连接
等待 5
step "切换到微信Webview"
等待 15
step "切换到美团外卖window"
end
复制代码
最终Feature内容以下:
Feature: 回归下单主流程
打开微信->进入首页->定位->进入自动化商户->下单->支付->订单详情
Scenario:
When 进入美团外卖M站首页
复制代码
相对于其余的UI测试框架,使用接近天然语言的描述,提升了Case可读性,编写上也没有其余框架那么复杂。固然UI自动化中仍是有一些小难点的,尤为是Hybrid应用,Appium目前还存在些对使用影响不大的Bug,在框架试用完成的状况下,将在微信入口体验优化项目结束后的进一步使用中去总结与完善。
都知道在美团点评,QA还担负着质量控制的工做,当功能+自动化+性能+其余测试工做于一身,并且是1:8的测试开发比下,如何去关注质量的改进?答案只有:工具化、自动化。开发这样一个小系统,技术方案选择上考虑主要是效率和学习成本,符合敏捷开发的特色,基于这些因素,应用了被称为“Web开发的最佳实践”的Rails框架。
Rails的设计有些颠覆传统的编程理念,CRUD的实现上不用说了,一行命令便可,数据库层的操做,经过migration搞定,在Mail,Job等功能的实现上也很是方便,框架都有对应的模块,而且提供了大量的组件,Session、Cookie、安全密码、邮件地址校验都有对应的gem,感受不像是在写代码,更像是在配置项目,不知不觉,一个系统雏形就完成了,整理了下项目中使用到的gem,主要有如下这些。
前端相关:
后端相关:
从搭建开发环境、写Demo,本身作产品、开发、测试、搭建生产环境、部署,边参阅文档边实现,总共18我的日左右,实现了平台基础功能、线上故障问题的管理及通知、测试报告的管理及通知、Sonar数据的抽取(Job及邮件)、Bug数据的抽取(Job)、自动化测试项目的接入、质量数据的Dashboard各种数据图表展现等功能,如下为系统功能的两个示例:
后台管理界面
线下缺陷周趋势
应用Rails,团队较快进入了能够经过数据进行质量分析的初级阶段,固然还有很长的路要走,在从0到1的这个过程当中,仍是较多地体会到了敏捷开发的特性,也充分感觉到了DRY理念。
以上为半年左右时间内,外卖上海QA团队在自动化工做上的一些实践,总的来讲,达到必定预期效果,整理这篇文章分享一些心得。所谓的主流与小众并不是绝对,主要从几个方面衡量:
固然应用“小众”技术,必然要面对很多挑战:如何迅速培养能掌握相关技术的同窗,与其余语言平台的衔接问题,面对团队的质疑等。尤为Ruby属于易学难精的那种,从脚本语言应用层次上升到动态语言设计层次仍是须要必定的学习曲线的,也就是说对于使用者来讲是简单的,对于设计者的能力要求较高,就像流传的Ruby程序员的进阶过程就是魔法师的养成史。
正由于有特点的技术,才值得去研究和学习,就像它的设计者所说,目的就是为了让开发人员以为编程是件快乐的事情。作了这么些年的测试,还可以不中止写代码的脚步,也是由于几年前开始接触Ruby。不论未来是否成为主流,它仍然是测试领域工具语言的不错选择,无论之后会出现什么样的技术,选型的标准也不会改变。技术的世界没有主流与小众,只有理解正确与否,应用得当与否。
写在最后
美团外卖上海研发中心长期招聘前端、客户端、后端、QA及数据、算法相关的工程师,欢迎有兴趣的同窗发送简历到huangzhuolin02@meituan.com。若是对咱们团队感兴趣,能够关注咱们的专栏。