“小众”之美——Ruby在QA自动化中的应用
前言
關于測試領域的自動化,已有很多的文章做過介紹,“黑科技”也比比皆是,如通過Java字節碼技術實現接口的錄制,Fiddler錄制內容轉Python腳本,App中的插樁調試等,可見角度不同,對最佳實踐的理解也不一樣。這里想要闡述的是,外賣(上海)QA團隊應用相對“小眾”的Ruby,在資源有限的條件下實現自動化測試的一些實踐與經驗分享。
背景
加入外賣上海團隊時,共2名QA同學,分別負責App與M站的功能測試,自動化測試停留在學習北京側接口測試框架的階段,實效上近乎為0,能力結構上在代碼這部分是明顯薄弱的。而擺在面前的問題是,回歸測試的工作量較大,特別是M站渠道眾多(4個渠道),移動端API的接口測試需區分多個版本,自動化測試的開展勢在必行。在這樣的條件下,如何快速且有效地搭建并推廣自動化測試體系?在過去對自動化測試的多種嘗試及實踐的總結后,選擇了Ruby。
Why Ruby?
簡單點說就是:并不聰明的大腦加上“好逸惡勞”的思想,促使我在這些年的自動化測試實踐中,不斷尋找更合適的解決方案。所謂技術,其本質都是站在別人的肩膀上,肩膀的高度也決定了實現目標的快慢,而Ruby正符合所需的一些特征:
脫離了開發語言的平臺,但在不關注白盒測試的情況下并無太多不妥。當Ruby用于測試開發,基本“屏蔽”了性能上的劣勢,充分展現了敏捷、易用的特點,也是選擇這一技術路線的主要因素。
接口自動化框架Coral-API
框架思路
接口自動化測試方案眾多,個人認為它們都有自己的適用的范圍和優缺點。UI類工具雖輕松實現無碼Case,但在處理接口變動和全鏈路接口流程上多少會顯得有些繁瑣(尤其在支持數據驅動需求下),過多的規則、變量設置和編碼也相差無幾;錄制類型的方案,更多還是適合回歸,對于較全面的接口測試也需要一定的開發量。基于這些權衡考慮,采用一種編碼盡可能少、應用面更廣的接口自動化框架實現方式,把它命名為Coral-API,主要有以下特點:
測試數據處理獨立
- 預先生成測試所需的最終數據,區分單接口測試數據(單接口數據驅動測試)與鏈路測試數據
- 通過命令行形式的語句解決了參數的多層嵌套及動態數據生成的問題
- Excel中維護測試數據,最終轉化為YML或存入DB,折中解決了JSON形式的數據難維護問題
學習成本低
- 框架提供生成通用結構代碼的功能,使測試人員更關注于業務邏輯處理
- DSL的書寫風格,即便沒有Ruby的語言基礎,也可以較快掌握基本的接口測試用例編寫
擴展性
- 支持Java平臺的擴展
- 支持HTTP/RPC接口,可根據開發框架擴展
- 框架基于Rspec,支持多種驗證方式(Build-In Matcher),及支持自定義Matcher,目前實現了JSON去噪的Diff,各種復合的條件比較
以單個接口測試編寫為例,下圖描述了具體流程:
從圖中可以看到,安裝了Coral-API的gem后,可通過命令行 “coral g {apiname}” ,通過模板來生成測試數據XLS及對應的數據處理文件(例如ApiOne.rb文件),修改并執行ApiOne.rb文件,則可以生成最終的測試數據(YML文件)及測試類和Case文件。如果開發框架支持(有途徑可解析出參數),則可以通過腳本直接生成整個服務下所有接口的測試代碼,實現自動化Case的同步開發。這種處理過程主要是一并解決了以下幾個問題:
假設有以下這樣一個接口請求格式,包含一個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 < ApiCaseBaseupdate 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 doupdate_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 endExcel作為Case的維護形式,缺點是Case較多情況下頻繁讀取比較影響時間。在這種情況下,考慮到把數據序列化到YML中,啟動執行時接口測試類自動與測試數據進行綁定。在Case中可以直接使用形如 DemoTest.request[1]的請求數據,提高了速度,結構上也清晰了不少。
接口測試類文件(HTTP接口調用為例)生成的模板如下,修改對應的接口信息即可,支持DB驗證(代碼塊p這部分是目前唯一需要寫Ruby代碼的地方,當然這是非必需項)。
require 'apicasebase'class PreviewTestinclude ApiTestBaseset_cookieset_domain "Domain_takeaway"set_port 80set_path "/waimai/ajax/wxwallet/Preview"set_method "get"set_sql "select * from table"p = proc do |dbres|## do something## return a hashendset_p pendTestCase文件如下,原則上無需修改,只需要在測試數據的Excel中編寫匹配規則及預期輸出,基本上實現了單個接口無編碼的數據驅動測試。
require 'Preview_validate'RSpec.shared_examples "Preview Example" do |key,requestData,expData|it 'CaseNo'+ key.to_s + ': '+expData['memo'] doresponse = PreviewTest.response_of(key)expect(response).to eval("#{expData['matcher']} '#{expData['expection']}'")end endRSpec.describe "Preview接口測試",:project=>'api_m_auto',:author=>'Neil' doPreviewTest.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' dolet(:wm_b_client) { WmBClient.new('自配') }before(:context) doinit_stependcontext "在線支付->商家接單->確認收貨->評價" doinclude_examples "OrderAndPay Example",1include_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: &DEFAULTheaders: '<%= $v926 %>'host: mobile.51ping.comport: '80'path: "/deliveryaddresslist.ta"search: "?geotype=2&actuallat=31.217329&actuallng=121.415603&initiallat=31.22167778439444&initiallng=121.42671951083571"method: GETquery: '{"geotype":"2","actuallat":"31.217329","actuallng":"121.415603","initiallat":"31.22167778439444","initiallng":"121.42671951083571"}'formData: "{}"scheme: 'http:'2:<<: *DEFAULTheaders: '<%= $v930 %>'3:<<: *DEFAULTheaders: '<%= $v940 %>'4:<<: *DEFAULTheaders: '<%= $v950 %>'5:<<: *DEFAULTheaders: '<%= $v990 %>'解決RPC接口測試
HTTP接口的測試框架選擇面還是比較多的,RPC調用的框架如何測試呢?答案就是JRuby + Java的反射調用,在Pigeon接口中我們已經試點了這種方式,證明是可行的,針對不同的RPC框架實現不同的Adapter(Jar文件),Coral-API傳參(JSON格式)給Adapter,Adapter通過解析參數進行反射調用,這樣對于框架來說無需改動,只需對部分文件模板稍作調整,也無需在Ruby中混寫Java代碼,實現了最少的代碼量—2行。
UI自動化框架Coral-APP
框架思想
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_objectsBefore{$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].downcasewhen "android"idempotently_define_singleton_method(element["OBJNAME"]){$driver.find_element(:"#{element["ATTRIBUTE"]}","#{element["ANDROID_IDENTITY"]}")}elseidempotently_define_singleton_method(element["OBJNAME"]){$driver.find_element(:"#{element["ATTRIBUTE"]}","#{element["IOS_IDENTITY"]}")}end end......support/pages為Page層,實現了每個頁面下的操作,目前把它實現為Kernel中的方法,采用中文命名,便于閱讀使用。
module Kerneldef 點擊我Elements.微信我.clickenddef 點擊收藏按鈕Elements.微信收藏.clickenddef 點擊收藏項Elements.微信收藏鏈接.clickenddef 點擊收藏中的美團外賣鏈接Elements.微信收藏鏈接URL.clickend endstep里的步驟我們可以這樣寫,封裝好足夠的公共步驟或方法,Case的編寫就是這么簡單。
When /^進入美團外賣M站首頁$/ do點擊我點擊收藏按鈕點擊收藏項點擊收藏中的美團外賣鏈接等待 5step "切換到微信Webview"等待 15step "切換到美團外賣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。
總結
以上是生活随笔為你收集整理的“小众”之美——Ruby在QA自动化中的应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 美团下一代服务治理系统 OCTO 2.0
- 下一篇: 阿里P8架构师谈:高并发与多线程的关系、