開源 | AREX:攜程新一代自動化回歸測試工具的設計與實現
作者簡介
haibing,攜程研發(fā)能效經理和SRE,關注自動化測試,能效提升方向的工具技術。
一、背景
隨著攜程機票BU業(yè)務規(guī)模的不斷提高,業(yè)務系統日趨復雜,各種問題和挑戰(zhàn)也隨之而來。對于研發(fā)測試團隊,面臨著各種效能困境,包括業(yè)務復雜度高、數據構造工作量大、回歸測試全量回歸、溝通成本高、測試用例數量多且難以復用、測試數據維護量大以及自動化用例管理等問題。每個都會影響測試團隊的效率和質量,給軟件研發(fā)過程帶來挑戰(zhàn)。
總結下來主要是兩個核心困難點:成本與復雜度。
成本方面,我們通常需要在成本和質量之間做出取舍,需要在快速迭代的同時保證質量,又需要在限定的投入下保證質量。
復雜度方面,當業(yè)務規(guī)則積累一段時間后,業(yè)務流程、規(guī)則、場景和數據處理的復雜度在疊加后呈二次或者指數等形式增加,給測試質量工作帶來很大的挑戰(zhàn)。
二、探索:自動化回歸測試的探索與實踐
為了應對這些挑戰(zhàn),我們在質量和測試工作中進行了一些持續(xù)性探索:
1)AUTO 測試平臺:常規(guī)的可編程的用例設計管理和執(zhí)行平臺。
2)錄制回放測試:引入 TCP Replay 進行回放測試,好處是可以簡化測試設計,缺點是沒有預期測試結果,需要人力去關注分析結果。
3)測試數據構造:通過腳本結合接口訪問生成數據,解決日常數據依賴的問題,但是構造的人力成本很高,而且是持續(xù)性的投入維護。
4)數據 MOCK 平臺:針對不同的場景建設多個 MOCK 平臺,解決測試數據問題。
5)各類覆蓋率平臺:度量測試的范圍與工作量。
6)優(yōu)化測試環(huán)境:建設基準測試環(huán)境和子環(huán)境,保證連調和測試的需要。
除此以外,還有很多其他測試框架、SQL日志分析、單元測試用例生成、Chaos 故障演練平臺等等的嘗試。
探索中遇到的問題
以上的各種探索都達到了一定的效果,但還是存在兩個問題:
1)自動化測試側重于自動化執(zhí)行,維護工作依舊是“手工”,產出比不是很樂觀。
2) 在需要構造大量測試數據、寫場景、回歸測試范圍大、發(fā)布頻繁的場景下,不管是手工測試還是自動化測試,開發(fā)測試都還是面臨著巨大的工作量,包括用例和數據的維護工作,測試的痛點并沒有有效解決。
新的建設目標
因此,研發(fā)團隊給我們提了新的建設目標:“質量要提升,要保證。費電可以,但不能廢人。”
基于以上種種探索經驗和新的目標,于是就有了“用真實的生產流量和數據進行回歸測試“的實現想法。最終,我們將這個想法轉化為具體的思路,并將其落地,建設了一個結合錄制+回放+比對的自動化測試平臺 AREX,現已開源(開源地址https://github.com/arextest)。
- 錄制:不只是錄制生產的請求,也錄制請求處理過程中涉及到的數據。
- 回放:不只是回放請求, 也把涉及的數據MOCK到應用里邊。
- 比對:用錄制回放的差異比對來代替測試斷言。
三、AREX平臺介紹
3.1 什么是AREX
AREX包含了Java Agent和前端、存儲、調度、報告、數據庫等服務組件 ,整體架構如下圖所示。
AREX回歸測試邏輯
AREX 回歸測試的實現邏輯是通過在生產環(huán)境錄制接近全量的業(yè)務場景請求和數據,然后在測試環(huán)境回放這些真實的請求和數據,對新版本的應用進行全面、快速的回歸測試。
目標場景
- 需要頻繁大量造數據的測試場景;
- 需要大量業(yè)務需要回歸測試的場景;
- 測試人力資源欠缺的開發(fā)場景;
- 頻繁發(fā)布,頻繁回歸測試的場景;
接下來從工作流程的角度,來講解AREX是如何工作的。
下圖是沒有接入AREX的應用的調用鏈。
圖片
當接入AREX后如下圖所示,上方是流量錄制過程,在正常的調用處理流程中,由AREX Java Agent 在調用鏈路上截取到數據并保存到AREX數據庫。
下方是回放流程,在回放請求時并不會真實地訪問第三方依賴,AREX截斷了調用鏈,由 Agent 從數據庫中讀取先前采集到的數據并返回給調用方。
AREX回放測試用例執(zhí)行結束后,由AREX調用比對SDK進行比對,輸出差異結果,生成測試報告。
四、AREX技術實現與優(yōu)化
流量錄制回放的概念雖然已經是老生常談,但是真正落地起來并沒有那么容易,里面有諸多難題需要解決。下面將詳細介紹我們團隊在實現AREX平臺時遇到的技術難點以及我們是如何解決這些問題的。
4.1 AREX gent錄制回放原理
以下通過一個簡單的函數,說明AREX Agent的錄制回放原理:
左側是轉換前的函數,右側是轉換后的函數。
在函數入口部分,做了回放判斷。如果需要回放,則使?采集的數據作為返回結果,也就是Mock。
函數出口部分做了錄制判斷,如果需要錄制,則將應用需要保存的中間數據,保存到 AREX 數據庫。
依賴包的注入,原理就是這么簡單。
4.2 AREX技術挑戰(zhàn)
AREX AGENT技術棧
由于性能好、且代碼更容易閱讀、容易理解,我們采用了ByteBuddy 庫實現字節(jié)碼修改。
此外還使用了 SPI(Service Provider Interface),這是 Java 提供的一套用來做擴展的 API,它可以用來啟用框架擴展和替換組件。我們的注入組件就是通過這個插件模式實現的。
實現TRACING
AREX的錄制功能需要將請求、應答、以及第三方的請求應答等都記錄下來,并且需要一個唯一的 Key 將這些數據串聯起來,這樣才能完整地作為一個測試用例,這個 Key 就是 AREX 的 Record ID。
以下這些類實現了 AREX 的調用鏈路。簡而言之,就是在調用鏈的入口處,Agent 會生成一個唯一的 Record ID 并將其保存到 Thread Local 變量中。在應用程序的函數頭和尾部注入 Agent 代碼,這段代碼會讀取 Thread Local 變量中的值,并將其與截取到的數據一起存儲。
解決調用鏈丟失
以上是 AREX TRACING 傳遞實現中比較普遍的基礎場景。除此以外,我們的應用中還有大量使用多線程、異步等技術的場景,這種場景下調用鏈會丟失,給數據的串聯帶來很大的困難。
為了解決調用鏈丟失的問題,我們實現了 Runnable,Callable,ForkJoinTask,Async Client 這些類的封裝。
基本原理就是在多線程代碼調用的地方,用我們封裝的類替換原有的代碼,而在封裝類中保留原有的功能,同時實現 AREX 的數據讀寫功能。
小竅門:Wrapper(包裝類) 是個關鍵詞,在代碼庫中搜索 Wrapper 可以看到所有 AREX 封裝類的實現。
實現錄制和回放注入
以下是一個實現錄制回放的代碼實例。
@Override
public List<MethodInstrumentation> methodAdvices() {
ElementMatcher<MethodDescription> matcher = named("doFilter")
.and(takesArgument(0, named("javax.servlet.ServletRequest")))
.and(takesArgument(1, named("javax.servlet.ServletResponse")));
return Collections.singletonList(new MethodInstrumentation(matcher, FilterAdvice.class.getName()));
}
當我們需要實現一個新依賴庫的注入時,是如何來實現這個插件呢?
首先,我們通過三個要素來定位到被注入的函數:
- 模塊(ModuleInstrumentation: FilterModuleInstrumentationV3):邏輯管理的概念,是將多個注入類、或者封裝類,放到一個模塊中。
- 類型(TypeInstrumentation: FilterInstrumentationV3):就是我們要定位到注入對象的類,即被注入的應用類。
- 函數(MethodInstrumentation):此函數要定位到注入修改的函數。
如上例中,我們要注入的類是 CacheAspectSupport 類,所在的模塊是 CacheModuleInstrumention 模塊,要注入修改的函數是 doFilter 函數。
接下來,通過三個步驟來實現函數注入代碼的功能。以下通過一個Mybatis的 Query() 函數來更直觀地看看AREX Agent如何實現代碼注入:
步驟一:將名為 METHOD_NAME_Query(字符串“Query”)的函數與類 QueryAdvice 關聯起來,QueryAdvice 是實現注入功能的類。
步驟二:QueryAdvice 類實現了函數 OnMethodEnter(),并標注了 ByteBuddy 的 Annotation。
步驟三:QueryAdvice 類會被注入到 Query 函數的頭和尾部。
如果需要回放,則將查詢到的數據存儲在一個本地變量中;如果不需要回放,則繼續(xù)執(zhí)行。
以下是 Query 函數在退出前的注入代碼:
- 如果 MOCK 結果符合條件,則返回 MOCK 數據;
- 如果當前狀態(tài)是錄制中,則將查詢 SQL+ 查詢結果原始數據保存到 AREX 的數據庫。
實現版本管理
流行的組件往往存在多個版本同時在不同的系統中使用,不同的版本實現方式差別可能很大,甚至不兼容。
針對這種問題,AREX 做到了多個版本的兼容。在應用啟動的時候,AREX Agent 會捕獲到所有的依賴包的信息,比如 JAR 包的 Manifest.MF 文件,從 Manifest 中獲取類庫的版本信息,然后根據版本信息來啟動對應的 AREX 注入代碼,由此實現實現了多個版本的兼容。
如下圖所示,設置了當前注入腳本適配的版本范圍,這樣 AREX 就可以在這些類加載前識別出應用依賴的組件版本,之后在類加載時進行版本的匹配,保證正確的代碼注入。
實現代碼隔離
由于 AREX 大多數的使用場景是在生產環(huán)境進行錄制,在測試環(huán)境進行回放,因此穩(wěn)定性至關重要。為了系統的穩(wěn)定性,防止 Agent 的代碼影響到被測應用的代碼執(zhí)行,AREX 實現了代碼隔離互通。
AREX核心JAR是在一個獨立的ClassLoader中加載,和用戶的應用代碼不互通。為了保證注入的代碼可以在運行時被正確訪問,對 ClassLoader進行了簡單的修飾,如下圖所示。
解決時間問題
攜程的很多業(yè)務場景是時間敏感的,經常會遇到錄制的時間在回放的時候已經過期了,業(yè)務邏輯走不下去,導致回放失敗的情況。
我們用自己實現的 currentTimeMillis() 代理了 Java 原有的 currentTimeMillis() 調用,時間的記錄和回放都將按照錄制當時的場景來執(zhí)行,從而實現了時間的 Mock。
在AREX Agent #182 針對此場景進行了詳細的描述:
解決緩存問題
實際應用中會使用各式緩存來提升運行時的性能,由于緩存數據的差異導致的執(zhí)行結果不一致,在錄制回放里邊是一個很大的問題。
AREX 提供了動態(tài)類 Mock 的功能,實現方法是將訪問本地緩存的方法配置成動態(tài)類,相當于你自定義了這個方法進行 Mock,會在生產環(huán)境錄制你配置的這個動態(tài)類方法的數據,回放相應的匹配出數據返回。
當然這種方式也存在不足之處:
- 緩存配置容易忽略,對回放通過率有很大影響;
- 每個應用都有自己的緩存實現,無法提前處理,需要人工參與,有配置成本。
4.3 AREX其他優(yōu)勢
支持寫接口測試
要驗證系統修改后的業(yè)務正確性,僅校驗返回結果是遠遠不夠的,通常還需要驗證中間過程數據的正確性,例如業(yè)務系統寫數據庫的數據內容是否正確等等。
針對這一點,AREX 在寫接口測試也做了完美支持。
AREX 在錄制和回放的過程中會記錄下新舊版本系統對外的數據庫請求,并將這兩個請求進行比對,如果存在差異則會在回放報告中進行展示
由于 AREX MOCK 了所有對第三方依賴的請求,支持數據庫、消息隊列、Redis 數據的驗證,甚至支持驗證運行時的內存數據,并且在回放的過程中不會真的產生對數據庫的調用,因此不會產生臟數據。
生產問題快速定位
在實際使用過程中,AREX 還可以用來實現生產問題的快速定位。
生產問題出現后因版本差異、數據差異等問題,導致開發(fā)人員難以在本地復現,進行 Debug 的成本很高,很費事費力。
利用 AREX,可以強制在生產環(huán)境錄制有問題的 Case(應答報文中會生成唯一的 Record ID),隨后啟動本地開發(fā)環(huán)境,發(fā)送請求的報文頭添加此 Record ID,就可以在本地復原錄制到的請求和數據,隨后利用本地代碼直接 Debug 生產問題。
五、AREX自動化測試的實施與展望
5.1 AREX在攜程的推行效果
各BU在接入AREX后,除去前期需要一些熟悉工具、配置的學習成本外,明顯減少了測試開發(fā)人員在自動化用例開發(fā)、數據 MOCK、構造數據方面的工作量,形成了良性循環(huán),減少了漏測并增加了覆蓋范圍,有效提升了產品的質量。
其中在兩個場景效果最顯著:
- 技術重構項目,特別是請求應答不修改的場景。這種使用場景下,幾乎不需要測試人員參與,開發(fā)人員自己就可以通過 AREX 進行快速自測,保證質量。
- 開發(fā)人員提升自測質量。
5.2 AREX優(yōu)化
初期AREX在攜程內部試用階段,大家對工具的評價還是很好的。但是,在擴大使用范圍、特別是在有其他團隊非主動接入時,就出現了各種問題:
- 誤報率高(時間、uuid、序列號等等),前期比對過濾配置多。
- 代碼變更的預期確認很麻煩,人工干預量大。
目前我們正在對AREX配置和比對能力進行重點優(yōu)化。
配置增強
現階段要保證高比對通過率需要大量的人工干預(比對忽略配置等),所以首先要做的就是降低用戶配置成本:
- 可視化直觀展示差異點
- 人工標記操作提升易用性
- 配置更新可重算和重新執(zhí)行
同類聚合
通過聚合的方式將同類的錯誤進行多維度聚合,方便開發(fā)人員觀察差異。最終達到大部分情況下,開發(fā)只要確認一個差異就可以去掉大部分的比對差異的效果
算法降噪
1)預分析降噪
預分析降噪是將錄制流量的生產版本發(fā)布到測試環(huán)境,對此版本進行回放并比對其差異,提前識別出類似于 token,序列號等的“噪聲”點。
之后將噪聲標記到規(guī)則庫,作為知識庫。
最后識別報文和數據的 Schema 變更,進行主動降噪,減少用戶手工配置的成本。
2)比對知識庫
比對是AREX的核心能力,但目前的比對是比較粗略的,誤報率較高,我們希望可以做到在迭代測試中,增加比對知識的積累(非人工干預),形成比對知識庫,幫助用戶準確識別有效差異。例如將 Schema 的定義轉成有效的比對規(guī)則。
精準測試
精準測試是為了縮小測試范圍,后續(xù)AREX中也會引入精準測試,主要目的是做到明確比對差異的來源。
我們計劃將代碼變更、代碼執(zhí)行鏈路與AREX的回放關聯起來,通過代碼變更與差異結果的雙向追溯,讓用戶“可觀察”地確認問題。首先識別差異是否是由代碼更新導致的預期中的差異,進一步主動過濾識別非預期問題的差異點。
經過對比對差異結果的優(yōu)化處理,可以有效地降低研發(fā)配置成本、提升差異結果的精確度,這才是AREX的自動化回歸測試真正可以落地的價值所在。
六、寫在最后
AREX經過不斷優(yōu)化,逐步達到了用真實流量和數據進行回歸測試的目標,降低了成本,提高了質量,達到了建設初期設定的目標。
當然還有很多需要優(yōu)化和提升的地方,包括算法、性能、支持范圍等等,需要進一步的優(yōu)化發(fā)展。也希望各位有志之士可以加入到我們AREX開源項目的共建當中。