如何將bug殺死在搖籃裏?
在歐洲中世紀的傳說中,有一種叫“人狼”的妖怪,就是人麵狼身。它們會講人話,專在月圓之夜去襲擊人類。而且傳說中對“人狼”用一般的槍彈是不起作用的,普通子彈都傷不到也打不死它,隻有一種用銀子作成的特殊子彈才能把它殺死。Brooks在他最著名的隨筆文章《No Silver Bullet》裏引用了這個典故 ,說明在軟件開發過程裏是沒有萬能的終殺性武器的,隻有各種方法綜合運用,才是解決之道。
那麼在軟件研發過程中,哪怕沒有銀彈,如何用各種方法去解決這些“人狼”帶來的威脅呢?阿裏巴巴在多年的研發過程中,又是如何對付這頭“人狼”?這一路走來,又有哪些方法和實踐沉澱?
阿裏巴巴研發協同平台(AONE)是阿裏一站式智能研發協同平台,目前為阿裏巴巴集團,下屬子公司以及生態合作夥伴提供從“需求->編碼->測試->發布->反饋”端到端的持續交付服務,並解決研發過程中跨角色、跨組織、跨地區的協作問題,在此基礎上通過數據驅動度量分析為組織效能提升提供決策依據,目前這一平台已經上雲對外提供服務,稱為阿裏雲研發協同RDC。(以下簡稱為RDC)
上圖是整個RDC的業務框架,RDC采用微服務的架構,從規劃到運營提供了業務全生命周期的服務,眾多的服務造成了RDC的複雜度,另一方麵作為一個麵向用戶的平台,RDC又涵蓋了阿裏三個層次的技術服務棧。
第一層,基礎資源層,包括idc機房、網絡設施、OS、Docker、數據庫等。
第二層,平台服務層,包括中間件、調度層、應用服務器等。
第三層,RDC研發協同平台,包括 項目、代碼、應用、測試、交付、運營的管理。
三個層次加起來涉及的核心應用達50+,隨便一個風吹草動就會對係統的穩定性造成影響,這些影響最終體現到麵向用戶的RDC,因此好的質量保障才能為用戶提供一個穩定,高可用性的研發協同平台。
總體采用 “事前預防”,“事中控製”,“事後總結改進”的思想,主要做的三件事情:
測試驅動持續交付
測試驅動持續交付,每一次的持續集成和發布都從單元測試,API測試,集成測試,UI測試進行自動化測試覆蓋;具體包括:單元測試,集成測試,WebUI測試,移動端UI測試,壓測,線上引流的API測試,線上冒煙測試。線上質量監控
對線上質量的保障,主要采用監控的方式,希望一有問題就能及時發現解決,避免問題的大麵積擴大。主要包括:
(1) 機器監控報警,業務數據監控。
(2) 對線上運行日誌的聚合分析,發現存在的錯誤。
(3) SLA數據質量提升,將業務數據可視化,指導改進方向。
(4) 麵向業務的監控和故障演練,通過對線上7*24的監控提前發現問題保障係統的穩定運行。研發質量提升
通過代碼審核,集團規約掃描,規範代碼和提升研發質量。
上圖是測試驅動的的持續交付流程,也是RDC各個應用在發布時的持續交付流程,通過RDC的發布功能實現。
(1) 開發在變更(Change Request)中提交代碼後,觸發單元測試,檢查代碼中基本的邏輯問題,實踐中單測的覆蓋率和維護由開發同學自己保證,目前RDC核心應用的單測行覆蓋率在50%左右。
(2) 部署測試環境,開發進行自測,主要對新的功能進行基本的驗證。測試環境與生產環境完全隔離,具有獨立的網絡環境,單獨的數據庫和中間件等依賴環境。
(3) 自測完成後發布預發環境,由測試同學進行集成測試,包括回歸測試和新功能的驗證,以及對核心接口的小壓測。預發環境與生產環境在同一個網絡中,共享同一個數據庫,數據隔離采用邏輯隔離,但是它具有一個獨立的依賴環境,如獨立的上下遊和中間件。
(4) 接著進入Beta測試環節,通過截取一部分生產環境的流量到Beta環境回放來對核心接口做進一步的驗證。Beta環境與線上環境完全相同,不同的是Beta環境不對外提供服務。
(5) Beta測試完成後,正式發布生產環境並對生產環境執行冒煙測試。
以上是一個變更的生命周期,也是一次完整的持續交付流程。得益於RDC發布係統和RDC實驗室的集成,我們可以在持續交付的流程中實現測試的卡點,測試不通過,無法進入下一個階段的發布。
以下內容重點討論測試方法和實現方案,以及如何通過RDC完成持續交付,不涉及代碼實現,部分服務也會在未來上雲提供給外部用戶試用。
4.1 分層的自動化測試
傳統的自動化測試更關注的產品UI層的自動化測試,而分層的自動化測試倡導產品的不同階段(層次)都需要自動化測試。在《google 測試之道》一書,對於google產品,70%的投入為單元測試,20%為接口、集成測試,10% 為UI層的自動化測試。如下圖的金字塔形,越往上的投入越低,按照代碼統計的測試覆蓋率也越低。
4.2 測試框架
- 測試框架的選型 在開始RDC的自動化前團隊對其他測試工具有了一定的積累,各種流行工具,數據驅動,關鍵字驅動工具,如Fitness,RobotFramework等,也調研過一些特定的測試工具如對http測試的Jmeter, Postman, SoapUI等。
這些工具普遍存在的一個問題是不夠靈活,工具本身比較重,調試麻煩;另一些比較輕量的工具又功能單一,針對性強,無法作為一個測試框架使用,測試場景比較簡單時非常好用,一旦場景變複雜,就帶來了大量的維護工作。
其次也開發過一些看起來很高大上的框架,有前端有後端,目的是減少測試人員的coding工作,一鍵完成測試,看起來很高大上。 但實際的問題是測試人員的主要工作變成測試框架的維護,最後的結果是業務綁定太強,無法推廣,而自己的用例覆蓋率也沒有上去。
最後選擇了TestNG作為主測試框架,其他的功能全部交給RDC完成。選擇TestNG的理由:
(1)是靈活,作為程序員可以用代碼實現靈活的場景組織和功能,隻要稍微二次開發一下。
(2)是跟很多框架一樣有Before,after,listener的概念,基於單元測試卻比junit強大,對場景的準備,還原,清理都能較好的處理。
(3)使用java,也許不是好的測試語言,但是團隊的技術儲備java是強項,擁有豐富的開源測試庫,能夠完成單測,API,集成測試,WebUI,移動UI的測試;另一個原因是RDC本身也采用Java開發。
(4)缺點是冗餘,作為一個測試框架,要實現測試比其他的測試框架要多一些代碼。但是作為一個需要長期維護的測試工程,這些冗餘也還可以接受。
如圖所示,整個測試框架分為四層,通過分層的方式,測試代碼更容易理解,維護起來較為方便。
第一層是基礎測試庫:包括selenium,macaca,ssh庫,httpclient,cli庫等。
第二層是服務層,相當於對測試對象的一個業務封裝,如http的測試,是對遠程方法的一個實現,對於頁麵測試,是對頁麵元素或操作的一個封裝。
第三層測試用例邏輯層,該層主要是將服務層封裝好的各個業務對象,組織成測試邏輯,進行校驗。
第四層測試場景,將測試用例組織成測試場景,實現各種級別cases的管理、冒煙,回歸等測試場景。測試執行
隨著測試用例的豐富和完善,測試時間會越來越長,我們的原則是盡量讓所有的cases在15分鍾內執行完,讓整個發布流程能快速迭代起來,在實踐中采用了兩個方法:
測試調度:主要用於集成測試和UI測試,因為這兩個是對一個係統整體進行測試,RDC采用微服務的架構,整個RDC應用被拆分成十幾個微應用,如果一個應用發布後測試過程中,另一個關聯應用也發布了,那麼這次測試結果的意義就不大。
因此在執行中加入了調度機製,對關聯應用進行調度,比如A、B是兩個互相關聯的應用,A應用發布後正在進行測試,B應用發布,這時我們的調度程序會將測試中斷,然後B應用發布完成後,重新開始測試,最後將結果返回給A、B兩個應用,同時完成卡點。
分布歸並: 在測試集合中我們將用例進行組合,保證每個測試集合都在15分鍾內,一旦發現時間增加則將測試集進一步拆分,然後將這些測試集並行執行,最後將整個結果歸並,既能壓縮時間,又能實現報告的統一。 所有用例在docker中執行,動態的分配測試資源。
4.3 單元測試與依賴隔離
關於單元測試的投入產出不同的公司團隊有不同的認識,實踐中認為單元測試的主要意義在於最低成本的發現問題,尤其對於代碼的改動,相比集成測試階段的問題更容易定位,並且良好的單元測試習慣能夠使開發在編寫代碼時更全局的考慮問題。單測中主要需要解決的問題是外部依賴的隔離。
外部依賴的隔離:Mock和Stub
由於單測主要檢查自身代碼,因此需要處理對外部依賴的隔離,常用的外部隔離方式主要有Mock和stub兩種方式,這兩種方式的主要區別是Mock關注對代碼邏輯的驗證,Stub關注狀態的驗證。單元測試中采用Mock的方式,原因是Mock能快速的實現單測邏輯,執行速度快,而Stub一般需要搭建複雜的模擬環境,維護成本較高。隔離工具:Powermock+Mock
通過這兩個工具可以Mock出 1. 構造函數、2. 靜態函數、3.枚舉實現的單例、4.選擇參數值作為函數的返回值、5.在調用的mock方法中,改變方法參數的值。
使用中Mockito可以滿足大部分的需求,但是它的實現機製是使用cglib來動態創建接口的類的實例,這種實現方式不能用於構造函數和靜態函數,因為這兩個需要使用類的字節碼(比如使用javassist),所以結合PowerMock一起。
單測用例
使用TestNG的注釋方式組織用例,通過Assert來校驗邏輯數據是否期望。接入RDC和覆蓋率收集
在RDC的【變更】中配置【持續集成】實驗室,在實驗室中配置單元測試實驗室,RDC會監控代碼提交事件,一旦提交則觸發單測的執行。
關於測試評價,目前也沒有特別好的評價體係,所以最常用的還是代碼覆蓋率,在工程的pom.xml中引入cobertura,排除不需要計入的第三方class,RDC單測實驗室中的代碼覆蓋率工具會自動統計覆蓋率。目前RDC的行覆蓋率在50%,據了解行業內做的好的一般在68%左右。
4.4 大集成測試
集成測試是整個持續交付中最重要的階段,穩定可靠的集成測試能夠減輕測試工程師的壓力,其次能促進開發和測試之間的相互信任,形成良好的互動。集成測試一般采用API的方式,在預發階段進行,選擇這一階段的原因是環境較為穩定,測試發現的問題能真正反映係統的問題。
集成測試的方式
如上麵分層測試的框架中,采用httpclient作為工具,將遠程的http服務封裝成本地服務,再進一步實現測試用例和測試場景。集成測試規模
集成測試按照集成的規模,分為小集成測試和大集成測試,小集成測試一般是在某幾個係統之間進行集成,大集成測試在整個係統中進行,這兩個主要區別是測試覆蓋和測試時間,我們使用最在靠近用戶UI一層的API進行大集成測試,這一層更能體現真實的體驗,能用最少的投入產生最大的價值。測試時間通過測試調度控製,每一個子係統發布時檢測是否有測試進行,如果有則停止再重新觸發。接入RDC
在RDC應用中有【測試驗證】,選擇【添加驗證點】,設置階段為預發集成測試(也可選擇其他階段),同時在此處可以設置卡點的條件,如設置測試通過率99%,當你的測試通過率小於這個數字時在發布流程的預發測卡點中將會報失敗,防止將測試驗證不通過的代碼發布到線上。
4.5 雲瀏覽器上的UI自動化測試
UI測試是一個很繁雜的事情,投入產出比不高,UI測試最大難點在穩定性和維護性方麵,因此我們在UI功能測試當中隻覆蓋主流程和最基本的頁麵檢查,主要對主流程進行覆蓋,如增、刪、改、查等這些出錯會造成係統不可用的功能進行覆蓋。
- UI測試的實現 UI測試采用selenium,通過selenium時將頁麵元素和功能進行抽象,稱為PageObject,如頁麵的增加按鈕,查詢按鈕的封裝作為PageObject放在服務層,而測試邏輯層則用這些服務組裝成需要的測試邏輯。值得分享的是使用selenium做UI測試時盡量采用css,css定位較為準確,相比xpath定位速度較快,對比的結果是采用css比xpath速度最快能到20倍,尤其在IE上。
UI測試另一個比較麻煩的事情是兼容性,由於各類瀏覽器的快速迭代經常造成selenium連接瀏覽器、獲取元素失敗。因此我們對selenium-grid進行了二次開發,搭建瀏覽器雲,根據指定的瀏覽器類型,版本、以及selenium的版本等在指定的瀏覽器上運行,滿足兼容性測試的同時,保證UI測試的穩定運行。
- 接入RDC 跟集成測試一樣,通過將UI測試配入發布流程進行卡點;實踐中是通過RDC實驗室的多階段將集成測試和API測試同時配到了預發環境,完成測試卡點。
4.6 全站式UI頁麵校驗
如上麵提到的UI功能測試主要針對主流程功能性的邏輯測試,無法對頁麵元素和展現進行校驗,因此我們對UI進行地毯式的校驗,采用的方法是 頁麵元素+圖像比對。
基本步驟:
第一步:使用selenium對RDC的各個頁麵進行爬蟲,深度為主域名以下三級Link,並遍曆出這些頁麵的元素,主要對文字、Button、Link、Form元素進行收集,保存到數據庫中。
第二步根據前麵錄製的Link,分別檢查頁麵能否正確打開,並校驗這些頁麵是否包含存在的元素。
第三步爬蟲腳本完善,由於個別頁麵的變化和對頁麵排版的要求,對導致測試失敗的腳本進行完善,去掉動態元素,由於動態元素不是很多在整個測試過程中影響不大;另一個是對展示要求較高的頁麵加入圖像比對,提前對屏幕進行截取(分塊截取,動態內容不做截取,這部分在前麵的功能測試中進行覆蓋),在回放中會對相應頁麵進行截取,使用工具sikuli將兩個截取的圖片進行比對。
4.7 核心接口壓測自動化
曾經有個故障,原因是開發的修改導致接口變慢。因此我們將壓測加入到常規的測試卡點中,在每次係統發布前都會進行一次壓測,將RT值進行對比。
- (1).壓測腳本采用jmeter,在jmeter中維護係統的關鍵接口,將jmeter的壓測結果用csv保存,設置測試執行的時間和並發線程數,一般設置為10個並發,時間為8分鍾。
- (2).準備基準數據,基準數據為係統在穩定情況下,將上麵的腳本執行10次,這10次執行的rt值為基準的數據,並將每個接口的基準數據保存在一個文件中。
- (3).測試結果對比,如此每次jmeter的運行後會產生一個csv的測試結果,將這些結果與之前的基準數據進行對比,如果結果落在的基準期間或者未超過最大基準數據10%(根據不同接口的響應時間這個百分比不一樣),則認為這次測試通過,並將這次通過的結果也插入到基準數據中,如此不斷更新基準數據,最終保留30次的執行數據。數據處理采用pyton的pandas。 如果測試不通過則發送對比圖片給相應的開發同學。 如下圖反映了一個接口的異常響應時間。
- 接入RDC 通過在預發階段新建【自定義實驗室】 可以將jmeter的運行腳本,結果處理以命令行的形式配置到實驗室完成測試卡點。
4.8 移動端真機測試
目前針對Android主要采用instrument的方式,對ios則采用自帶的的sdk的方式,隨之而來的問題是針對不同平台需要兩套測試代碼。開源的macaca和appium解決了這個問題,這裏選用macaca來進行測試,相比較Appium優點是運行穩定,去除了appium老版本Android的支持,而且macaca有專門的團隊進行支持。使用macaca能夠像selenium一樣對移動端的界麵進行測試。
真機測試環境搭建
起初為了節省成本,采用了android和ios的模擬器,實踐中模擬器的可用性太差,各種啟動慢,異常退出,基本無法使用。最終采用了jenkins+服務器+真機的集群方式,服務器安裝jekins的slave,ios和android環境,手機接入服務器,並在服務器上安裝macaca服務。測試執行
如上采用jekins中央集群的方式,通過jenkins來配置要進行測試的機型,jenkins根據機型查找手機所在的服務器和手機的uid,並將測試代碼下載到該服務器,執行測試腳本,並返回結果。接入RDC
目前RDC尚未提供真機環境,因此我們使用Jenkins管理真機環境,然後通過調用Jenkins的Rest接口,觸發測試在真機環境上運行,執行完成後解析Jenkins的報告為RDC格式的結果,即可在RDC的實驗室中展示測試結果,進而實現對發布流程的卡點。
4.9 AOP錄製回放進行API測試
傳統的API測試,一個接口一個接口的編寫代碼,準備測試數據,添加驗證點,工作量大,覆蓋不全,測試效果不明顯。這裏我們引流生產環境的流量來進行API測試,原理是AOP的方法攔截。主要有三個步驟,線上流量複製,回放,寫操作的Mock。
錄製回放
錄製:使用ASM修改被測接口的字節碼,侵入生產環境(生產環境提供一台機器),在每個被測方法執行前攔截得到需要的參數,方法結束時攔截得到返回的響應值。將錄製的返回值和方法序列化後保存到數據庫,需要注意的是對寫操作和外部係統的調用可能會對生產數據造成破壞,所以對接口內部的方法依次進行跟蹤,跟蹤到寫操作時,錄製寫操作返回的數據。回放:
同樣采用ASM在測試環境(Beta環境)修改被測接口的字節碼,觸發接口的調用,在被測接口中使用上麵錄製的參數,接口執行完成後將錄製的結果和這次執行的結果進行對比,結果一致則測試通過。
Mock:對於寫操作,由於我們采用的Beta環境和生產環境采用同樣的數據庫和外部係統,因此到寫操作不能真實的寫入數據,需要在寫操作的方法中直接返回上麵錄製的寫操作數據。接入RDC
在上麵的錄製環境和生產環境中,安裝agent開啟http服務,跟RDC實驗室進行通信並控製測試的執行,最後的返回結果被RDC解析,從而實現發布卡點的功能。目前該係統正在改造,改造後將對雲上用戶開放。
4.10 測試報告
測試報告同樣采用了分層的報告,分為:實時報告,單次執行報告,統計分析報告。
實時報告
主要針對線上用例的冒煙測試,原因是測試用例全部跑完需要一定的時間,而對於線上我們希望能盡快的將錯誤反饋給開發並解決,所以一旦有錯誤就會實時將錯誤信息和的現場通過釘釘發到開發群。單次執行報告和後台日誌
單次的執行主要是對這次運行的結果做展示,包括測試用例,測試步驟,截屏等。 在發送報告的同時我們會拉取測試時間段應用的後台日誌,這些日誌包括了錯誤所在的機器,出現的時間點,stack以及,各個應用接口間的數據傳遞,通過這些日誌能方便開發同學快速定位問題。統計分析報告
在測試過程中同樣需要對測試代碼的改進和測試穩定性的提高,每次測試完成後我們會將這次測試的結果解析發送到我們的一個測試報告係統中, 對通過率,失敗次數等進行統計分析,通過一個時期的分析來找出值得改進的測試用例或係統功能,從而提高測試穩定性和係統穩定性。
5.1機器和業務監控
對於機器監控有很多開源的方案,在此不再詳訴,一般來說主要采集機器的CPU,內存,TPS,JVM等指標,一旦某個指標跨過紅線,及時做出報警。
對於業務數據比較複雜,根據RDC的業務有幾十種不同的監控指標,這裏我們主要采用的是日誌打點的方式,RDC的各個子係統的都對輸出日誌進行了改造,生產環境的每台機器會有一個專門解析日誌的agent,這些agent增量的實時監控業務日誌,並將日誌解析得到跟業務相關的數據,發送給監控係統,從而監控係統能得到某個點或者某個時間段內的業務情況,根據劃定的業務指標紅線給出報警。 該係統目前正在進行改造,之後將提供給雲上用戶使用。
5.2線上日誌聚合分析
由於現在係統都采用微服務的方式,係統拆分較細,每個微服務又都采用集群的方式運行,因此係統隻要正常運行,後台發生了多少錯誤幾乎沒有人關心,而這些錯誤有可能是導致故障的潛在隱患。
我們采用了阿裏雲的sls日誌收集功能,將RDC相關應用的日誌接入到sls,每天晚上00:00按應用對RDC當天產生的日誌進行聚合分析,目前通過一定的算法對錯誤信息進行聚合,發現問題和缺陷,自動提交bug,並發送當天的統計報告。 目前通過日誌的聚合分析,RDC修複了大量穩定性方麵的錯誤,錯誤數由之前每天的十萬量級降低到現在的千級。
5.3 SLA服務數據驅動質量
將用戶體驗量化,如何為用戶提供極致體驗,一直是我們努力的目標,然後由於RDC上下遊係統較多,如何評價現有係統,采用將服務體驗數字化,通過將每一層的服務數字化來找到問題的瓶頸。
數據的收集采用了兩種方式,一種是直接從數據庫中提取數據,另一種是通過打點的數據提取業務數據。將這兩種數據經過計算統計報表的形式展現,從而清晰的知道目前係統的情況。
通過這些數據能夠了解到整個係統的瓶頸,從全局了解係統和用戶感知的差距,並指定目標和改進計劃。 目前的問題是無法智能的拿到這些失敗或錯誤的原因,比如出版本失敗率0.14%,這0.14%是什麼原因導致的需要花很大的精力排查,如何智能的找到問題,減少排查工作是下一步要做的工作。
5.4 麵向業務的穩定性監控和故障演練平台
- 功能監控,將RDC相關係統的冒煙用例在線上7*24小時連續不斷運行,來及時發現線上的問題,盡量在先於用戶發現問題避免故障麵的擴大。
- 頁麵監控,同樣對RDC的頁麵7*24小時訪問,看這些頁麵是否能夠正常訪問,鏈接是否被修改。
- 故障演練,根據RDC係統和業務邏輯製定專門的故障演練方案,將這些演練場景驗證點用自動化實現並接入到故障演練平台。目的是能實現可靠地自動化故障演練,從而讓演練按需隨時進行,發現複雜場景下潛在的故障。
研發質量的提升目前主要包括代碼審核、代碼質量檢查好研發效能數據的回溯。
- 代碼審核:在RDC上創建變更時會指定響應的代碼審核人員,發布時指定的開發同學會對發布的代碼進行審核,除了保障質量外,最重要的是通過審核提供開發人員的代碼質量和代碼規範,減少較低級的錯誤。
- 代碼掃描:我們Sonar掃描+阿裏巴巴集團規約掃描的方式,要求對Blocker和Critical的問題必須修複後才能上線,其它級別的缺陷不能超過20%。
- 研發效能回溯:RDC提供的度量功能是一個團隊的研發情況看板,它能夠反映研發團隊在處理需求,缺陷、發布、回滾以及有效代碼量等方麵的情況,從多維度的統計一個團隊的研發能力和研發效率。通過每周回溯這些數據,找出團隊存在的問題,促進研發效能的提高。
對於自動化測試,希望能用更有效的發現問題,尤其在UI測試上,考慮如何用最低的成本獲得最大的回報,進一步完善爬蟲式測試回放的工具,增加測試的覆蓋率。
線上質量的監控,希望能夠對故障進行預測,目前我們做到的還是對故障的處理,比如故障發生10s內發現,3分鍾能響應處理完成,但是如果能夠對故障進行預測將極大的降低係統風險,所以下一步工作是對海量錯誤日誌,故障數據,用戶反饋數據,監控數據進行數據挖掘分析,並結合實時監控建立自學習的預警係統。
6月29日13:40,“首屆阿裏巴巴研發效能嘉年華”直播活動正式啟動!阿裏巴巴資深技術專家,十年敏捷教練,一線的實踐專家,帶你全麵感受研發全生命周期,切身體驗阿裏巴巴多年成熟的研發流程與經驗。
最後更新:2017-06-29 11:31:57