隨著互聯(lián)網技術、持續(xù)交付(Continuous Delivery)[1]的發(fā)展,分布式系統(tǒng)和應用已成為當前軟件開發(fā)的主要方向之一。與集中式的串行程序相比,現(xiàn)代互聯(lián)網服務通常都是復雜的大規(guī)模分布式系統(tǒng),這些系統(tǒng)由多個軟件模塊構成。由于分布式系統(tǒng)軟件的模塊可能由不同的團隊、使用不同的編程語言開發(fā),系統(tǒng)測試需要多團隊協(xié)作,將企業(yè)內多種異構服務集成,給分布式系統(tǒng)測試工具帶來了新的挑戰(zhàn)。
在微服務架構[2]下,系統(tǒng)進一步細化為面向特定服務的子系統(tǒng),結構圖如圖1所示。一個系統(tǒng)功能往往需要組合調用多個接口完成,既要測試每個接口的正確性,也要從功能角度編排所依賴的接口進行驗證,系統(tǒng)復雜度的增長,使得測試用例數(shù)量相應增加。
圖1 微服務架構
另一方面,企業(yè)為了在市場競爭中贏得優(yōu)勢,要求更短的需求交付周期,及早推出新功能,而測試是檢驗系統(tǒng)是否滿足需求及上線標準的重要環(huán)節(jié)。Google、Facebook等公司依托其強大的自動化測試系統(tǒng),能實現(xiàn)一天數(shù)次的發(fā)布[3]。
目前已有的自動化測試工具如JUnit[4]、Postman、Selenium[5]、Appium[6]等,都無法很好支持企業(yè)內部異構服務集成、多團隊協(xié)作、海量測試用例執(zhí)行的場景。本文提出的aSIT是一套面向接口的分布式自動化測試系統(tǒng),能較為有效地解決企業(yè)自動化接口測試面臨的問題。
持續(xù)交付是一種軟件工程方法,讓軟件產品的產出過程在一個短周期內完成,以保證軟件可以穩(wěn)定、持續(xù)的保持在隨時可以釋出的狀況。它的目標在于讓軟件的建立、測試與部署變得更快以及更頻繁,同時還能保持高質量。
持續(xù)交付的核心措施是,代碼集成到主干、發(fā)布之前,必須通過自動化測試。自動化測試不通過,則不能進行交付。
持續(xù)交付對測試過程提出了更高要求:測試用例低成本、可重復、頻繁執(zhí)行,并且在不同測試環(huán)境下穩(wěn)定運行。
以JUnit、TestNg為代表的開源單元測試[7]框架,為開發(fā)人員編寫和運行可重復的單元測試用例提供支持。但用戶需要使用跟被測系統(tǒng)相同的開發(fā)語言,編寫白盒測試代碼,要求用戶具備一定的編程能力。單元測試的粒度要求盡可能小,一般在函數(shù)級別,用于驗證函數(shù)本身的邏輯正確性,對外部系統(tǒng)的依賴采用Mock[8]來模擬。
Postman是一款用于網頁調試與發(fā)送HTTP請求的工具,可用于編寫、管理、執(zhí)行HTTP接口的測試用例。但其只支持HTTP協(xié)議接口,而分布式系統(tǒng)間通訊廣泛使用的Dubbo、gRPC、thrift[9]等RPC協(xié)議尚未支持。
Selenium、Appium為代表的GUI界面測試工具,可以讓錄制用戶在界面的操作,并進行重放。由于是在界面上進行操作,使用相對簡單。但要求系統(tǒng)必須有用戶界面,不適合中臺、基礎子系統(tǒng)的測試。同時用戶界面往往頻繁,做好的用例可能在下個版本就不適用,導致維護自動化用例的成本較高。
上述3類工具分別代表分層測試中的單元測試、接口測試、GUI測試。在Google等頭部IT企業(yè),往往沒有專職的測試人員[10],軟件質量由開發(fā)人員保證。開發(fā)人員編寫大量的單元測試用例,追求代碼級的覆蓋率。因此形成了“金字塔模型”,即單元測試占絕大部分、接口測試略少、GUI測試最少。
但國內大部分的IT企業(yè),特別是傳統(tǒng)行業(yè)的IT部門,開發(fā)人員、測試人員間分工明確,往往還分屬不同的團隊。測試人員大多數(shù)不具備使用開發(fā)語言編寫高質量單元測試用例的能力。因此,推行上述金字塔模型很難落地。
考慮到GUI測試的局限性,從接口測試切入自動化測試是較為可行的辦法。主要原因是:① 接口測試投入產出比較高,可以實現(xiàn)較高的自動化率;② 可以幫助加強開發(fā)與測試人員之間的協(xié)作,提高測試質量。接口測試需要開發(fā)跟測試人員共同定義,因為開發(fā)知道內部實現(xiàn)的細節(jié),測試掌握業(yè)務場景。將測試的重心放在接口測試上,并且提倡高度自動化,單元測試、GUI相對較少,形成“橄欖球模型”如圖2所示。
圖2 分層自動化:金字塔模型與橄欖球模型
此外,一個業(yè)務流程的實現(xiàn)往往涉及不同系統(tǒng),需要不同團隊合作,要求測試系統(tǒng)支持多團隊協(xié)同。
企業(yè)內部異構服務的示意如圖3所示。系統(tǒng)A使用Java開發(fā),系統(tǒng)B使用Go語言開發(fā)。A對外暴露HTTP接口,A通過gRPC調用B。A、B由不同團隊負責測試。B有兩套環(huán)境,網絡隔離。
圖3 企業(yè)內部異構服務
上述例子要求測試系統(tǒng)需支持:
① 調用不同協(xié)議的接口
② 多人協(xié)作,數(shù)據(jù)互不影響
③ 一套測試系統(tǒng)在多套環(huán)境執(zhí)行用例
基于上述考慮,我們實現(xiàn)了一個接口自動化測試系統(tǒng)aSIT,aSIT的主要功能如下。
① 在圖形化界面錄入、管理用例,定義數(shù)據(jù)初始化和斷言操作;
②對HTTP協(xié)議接口,支持從Swagger導入接口定義;
③ 對RPC協(xié)議接口,支持通過接口包、IDL定義文件導入接口定義;
④ 將用例編排為用例集,用于全量回歸、新需求驗收等不同場景;
⑤ 不同用戶操作同一用例時,用例數(shù)據(jù)實現(xiàn)隔離,避免相互影響;
⑥ 系統(tǒng)本身基于分布式設計,可橫向伸縮,支撐不同用例數(shù)量執(zhí)行;
⑦ 按優(yōu)先級執(zhí)行用例,資源受限時保障高優(yōu)先級用例先執(zhí)行;
⑧ 支持通過信號觸發(fā)或定時觸發(fā)用例執(zhí)行;
⑨ 一套系統(tǒng)支持企業(yè)內部多套環(huán)境用例執(zhí)行。
aSIT的系統(tǒng)結構如圖4所示,主要分為用例管理與用例執(zhí)行兩大部分。
圖4 aSIT系統(tǒng)架構
(1)用例管理(Test Case Management):供用戶編寫、維護用例,查看用例執(zhí)行結果,隔離用戶數(shù)據(jù)。
(2)用例執(zhí)行部分:用戶執(zhí)行用例時,Execution Dispatcher負責根據(jù)用例數(shù)據(jù)和待執(zhí)行環(huán)境信息,組裝為執(zhí)行期數(shù)據(jù),附上執(zhí)行優(yōu)先級,發(fā)送到消息隊列Priority Message Queue;Execution Agent從消息隊列獲取用例執(zhí)行任務,按運行期數(shù)據(jù)調用用例中的接口,將返回結果加入運行期數(shù)據(jù),并發(fā)送到Priority Message Queue;Assertion模塊從Priority Message Queue獲取用例接口調用結果,并與斷言進行比對。
4.1.1 用例定義
首先將用例定義為以下七元組:
其中,Protocal為接口所用的協(xié)議;Env為接口環(huán)境信息;Init為數(shù)據(jù)初始化步驟,可為接口用例準備數(shù)據(jù);Identifier為接口名;Input為接口輸入參數(shù)值,可引用Init中獲取的數(shù)據(jù);Assertion為斷言信息。
基于用例我們定義用例集:
Ordered::=True | False
用例集是一組用例的集合,可引用多個用例或其他用例集,并指定其中的用例是否串行執(zhí)行。Ordered默認值為False,適用于用例間無相互依賴的情況。對于用例間存在依賴的場景,如“下訂單”用例執(zhí)行前需要先執(zhí)行“登錄”用例,則將Ordered設置為True。
在用例執(zhí)行階段,僅Ordered為True的用例集中,用例需串行執(zhí)行,其他用例都是并發(fā)執(zhí)行性,降低用例執(zhí)行耗時。
4.1.2 多團隊協(xié)同使用
對于多人同時編輯同一用例的場景,我們借鑒了Git代碼分支管理的思路,將用例分為主干用例與分支用例進行管理。主干用例是經驗證可重復穩(wěn)定運行的用例,可供他人使用,往往用于回歸測試。分支用例是未經審核的用例,用于個人調試。分支用例可申請?zhí)嵘秊橹鞲捎美?/p>
如圖5所示,測試人員A希望修改主干用例1以覆蓋新需求的改動,先創(chuàng)建用例1的一個副本(用例1a),并在用例1a上進行修改。測試人員A將用例共享給開發(fā)人員B,讓其檢查測試用例。檢查無誤后,測試人員A申請將用例1a合入主干。測試人員C希望基于主干用例1創(chuàng)建新的用例,于是創(chuàng)建用例1的一個副本(用例1b),修改其中的輸入參數(shù),提交申請成為主干用例2。在各自修改其分支用例期間,主干用例不會受到影響,仍正常用于回歸測試。
圖5 團隊協(xié)作與用例分支
用例執(zhí)行部分,我們利用消息隊列(Priority Message Queue)將執(zhí)行數(shù)據(jù)準備(Execution Dispatcher)和具體的執(zhí)行操作(Execution Agent)解耦。Priority Message Queue將執(zhí)行任務按優(yōu)先級排序,Execution Agent會先取到優(yōu)先級高的任務進行處理。
單個用例執(zhí)行時,會默認生成一個僅包含自身的虛擬用例集。用戶在界面發(fā)起執(zhí)行的用例集,其默認優(yōu)先級為AboveNormal。從后臺發(fā)起執(zhí)行的用例集(集成流水線觸發(fā)、定時觸發(fā)等)默認優(yōu)先級為Normal。這樣的默認設置是考慮到:用戶在界面發(fā)起執(zhí)行用例時,往往是單個調試,期望盡快獲得結果反饋;而后臺觸發(fā)的大批量用例執(zhí)行通常是版本發(fā)布前跑的回歸用例,對結果返回的時效要求相對較低。當然,用戶可以在界面調整用例集的執(zhí)行優(yōu)先級,滿足其需要。
多個Execution Agent通過Multicast相互通訊,基于Bully算法[11]組成集群。由于Execution Agent集群并不需要強一致性,因此沒有使用更復雜的Paxos算法[12]。
算法1 Execution Agent集群Leader選舉流程
Leader負責從Priority Message Queue獲取待執(zhí)行的用例,并分配給自己和Follower執(zhí)行用例。Execution Agent集群是可橫向伸縮的,當待執(zhí)行的用例數(shù)量巨大,已有集群執(zhí)行用例的速度無法滿足要求時,可增加Execution Agent部署數(shù)量,新增的Agent將自動加入集群,處理測試用例。
如圖6所示,當aSIT需要執(zhí)行網絡隔離環(huán)境下執(zhí)行測試用例時,由于環(huán)境僅在實際調用接口時有影響,因此,只需要在每套環(huán)境部署Execution Agent負責該環(huán)境的用例執(zhí)行即可。Execution Agent Leader開通到Priority Message Queue的網絡訪問策略,即可獲取到待執(zhí)行的用例數(shù)據(jù)。
圖6 網絡隔離環(huán)境部署圖(執(zhí)行部分)
aSIT推出市場超過兩年,目前使用的客戶涉及通信、金融、地產、新零售等多個行業(yè)。本節(jié)我們回顧某金融企業(yè)引入aSIT后的測試效能提升情況。該企業(yè)互聯(lián)網金融項目組,開發(fā)、測試人員比例為4:1,每個版本迭代周期為1個月。引入aSIT前,測試團隊一直采用手工測試,執(zhí)行測試用例耗費大量時間,每個迭代的測試周期約半個月。
表1是引入aSIT后,跟蹤10個迭代,測試團隊工作內容分配情況。在最初3個迭代,部分功能測試、回歸測試用例仍依靠手工執(zhí)行,測試人員大部分時間投入在用例執(zhí)行,設計自動化用例、將回歸測試用例改造成自動化用例的投入逐步增加。從迭代5開始,項目組加大了存量手工測試用例改造為自動化測試用例的投入。最終絕大部分測試用例可自動化執(zhí)行,僅剩少部分必須人工執(zhí)行的用例,測試人員主要精力投入到測試用例的設計與優(yōu)化中。
表1 測試團隊工作內容分配
引入aSIT前,該回歸測試均由人工執(zhí)行,用例數(shù)24個,僅有限覆蓋主干流程。隨著在aSIT上維護自動化用例,越來越多的高質量用例成為主干用例,并加入到回歸測試集,回歸測試覆蓋更多的業(yè)務場景。如圖7所示,迭代0代表使用aSIT前,回歸測試數(shù)目為24個;到第10個迭代,回歸測試用例數(shù)已達744個。
圖7 回歸測試用例數(shù)
aSIT大大降低了用例執(zhí)行耗時,缺陷驗證時間由原先接近1天,降低到5分鐘以內。
需求交付耗時,是指從需求提出到需求發(fā)布的時間。我們按每3個月滾動統(tǒng)計平均需求耗時,平均需求交付耗時從約87天下降到32天,如表2所示。
表2 需求平均交付耗時
在使用aSIT的過程中,我們認識到,aSIT作為一款接口自動化測試工具,其應用效果還與如何使用此工具密切相關。我們從客戶落地的實際經驗中總結出如下最佳實踐。
① 在集成流水線中自動化調用aSIT執(zhí)行自動化測試
除了在aSIT界面手工發(fā)起或配置定時任務定時觸發(fā)測試執(zhí)行外,建議在集成流水線中調用aSIT提供的用例執(zhí)行接口,確保每次集成時測試用例100%通過,否則中止集成流程。
② 定期審查測試用例,確保用例健壯性
自動化測試的有效性是需要通過維護去保持的,定期重新審查本是一種測試用例的必要維護手段。審查應關注測試數(shù)據(jù)的初始化和使用是否合理,是否會破壞測試環(huán)境中數(shù)據(jù)的健康度甚至帶來環(huán)境故障,驗證點是否足夠精確等。
③ RPC接口包不要包含具體實現(xiàn)邏輯
從軟件工程角度看,提供給調用方的接口包應只定義接口,不能包含接口的具體實現(xiàn)邏輯。有的開發(fā)人員工程意識不強,圖方便把接口及其實現(xiàn)一同編譯到接口包中,導致在aSIT中通過解析接口包導入接口定義時出現(xiàn)問題,如接口包體積比僅包含接口定義大很多(曾碰到用戶上傳1個約100 MB的jar文件),導致解析時間長,觸發(fā)了aSIT的超時保護機制;又或者實現(xiàn)邏輯有依賴其他程序包,解析時缺少依賴信息。
④ 開發(fā)、測試一同完善測試用例
開發(fā)定義出接口后,測試人員可以隨即介入,在aSIT上創(chuàng)建測試用例,并共享給開發(fā)人員。開發(fā)人員在調試程序時,就可以持續(xù)補充用例,基于測試結果完善程序。在此過程中,測試人員也能優(yōu)化用例。這樣實際上自然實現(xiàn)了測試前置。
我們設計并實現(xiàn)了接口自動化測試工具aSIT,并在本文中匯報了我們應用aSIT的效果,總結最佳實踐。微服務、分布式架構為系統(tǒng)測試帶來挑戰(zhàn),需要自動化測試才能支持需求快速交付。本文提出,從接口測試切入自動化測試是適合我國企業(yè)現(xiàn)狀的。aSIT設計時參考現(xiàn)有的自動化測試理念,考慮如何解決企業(yè)面臨的多團隊協(xié)作、接口協(xié)議不統(tǒng)一、測試環(huán)境隔離等問題。在實際使用中,能夠解決上述問題,并提升企業(yè)的測試效能。
通過自動化測試提升企業(yè)的研發(fā)效能是DevOps[13]的重要部分,后續(xù)工作將會研究如何根據(jù)生產環(huán)境接口調用自動生成測試用例、如何度量接口測試覆蓋面、更科學評價自動化測試對效能提升等。