測試三明治和雪鴞探索測試

測試金字塔理論被廣泛應用於計劃和實施敏捷軟件開發所倡導的測試自動化,並且取得了令人矚目的成就。本文嘗試從產品開發的角度出發,結合Kent Beck最近提出的3X模型和近年來迅速發展的自動化測試技術,提出並討論一種新的測試層級動態平衡觀:

三明治模型。同時,為了應對端到端測試在實踐中面臨的種種挑戰,設計並實現了一種面向用戶旅程的端到端自動化測試框架——雪鴞。實際項目經驗表明,雪鴞能夠顯著提升端到端測試的可維護性,減少不確定性影響,幫助開發人員更快定位和修復問題,對特定時期的產品開發活動更具吸引力。

背景

測試金字塔

按照自動化測試的層級,從下至上依次為單元測試、集成測試和端到端測試,儘量保持數量較多的低層單元測試,以及相對較少的高層端到端測試,這就是測試金字塔理論。隨著敏捷軟件開發的日益普及,測試金字塔逐漸為人所知,進而得到廣泛應用。Mike Cohn、Martin Fowler以及Mike Wacker等先後對測試金字塔進行了很好的詮釋和發展,其主要觀點如下:

  • 測試層級越高,運行效率就越低,進而延緩持續交付的構建-反饋循環。

  • 測試層級越高,開發複雜度就越高,如果團隊能力受限,交付進度就會受到影響。

  • 端到端測試更容易遇到測試結果的不確定性問題,按照Martin Fowler的說法,這種結果不確定性的測試毫無意義。

  • 測試層級越低,測試的代碼隔離性越強,越能幫助開發人員快速定位和修復問題。

3X模型

2016年起,敏捷和TDD先驅Kent Beck開始在個人Facebook主頁撰寫系列文章,闡述產品開發的三個階段——Explore、Expand和Extract,以及在不同階段中產品與工程實踐之間的關係問題,即3X模型。近二十年軟硬件技術的飛速發展,使得軟件開發活動面臨敏捷早期從未遇到的市場變革,而根據在Facebook工作的經歷,Kent Beck把產品開發總結為三個階段:

  • 探索(Explore),此時的產品開發仍處於非常初期的階段,仍然需要花費大量時間尋找產品和市場的適配點,也是收益最低的階段。

  • 擴張(Expand),一旦產品擁有助推器(通常意味著已經找到了市場的適配點),市場需求就會呈現指數級上升,產品本身也需要具備足夠的伸縮性以滿足這些需求,由此收益也會快速上升。

  • 提取(Extract),當位於該階段時,公司通常希望最大化產品收益。但此時收益的增幅會小於擴張階段。

測試三明治和雪鴞探索測試

(3X)

Kent Beck認為,如果以產品是否成功作為衡量依據,那麼引入自動化測試在探索階段的作用就不大,甚至會延緩產品接受市場反饋循環的速度,對產品的最終成功毫無用處,還不如不引入;當位於擴張階段時,市場一方面要求產品更高的伸縮性,另一方面也開始要求產品保證一致的行為(例如質量需求),那麼此時就該引入自動化測試來保證產品的行為一致性;當產品最終處於提取階段時,任何改動都應以不犧牲現有行為為前提,否則由此引發的損失可能遠高於改動帶來的收益,此時自動化測試就扮演了非常重要的角色。

測試工具爆炸式增長和綜合技能學習曲線陡升

根據SoftwareQATest網站的歷史數據,2010年記錄的測試工具有440個,共劃分為12個大類。這個數字到2017年已經變為560個,共15個大類,且其中有340個在2010年之後才出現。也就是說,平均每年就有50個新的測試工具誕生。

面對測試工具的爆炸式增長,一方面所支持的測試類型更加完善,更加有利於在產品開發過程中保證產品的一致性;另一方面也導致針對多種測試工具組合的綜合技能學習曲線不斷上升。在實踐中,團隊也往往對如何定義相關測試的覆蓋範圍感到不知所措,難以真正發揮測試工具的效用,也很難對產品最終成功作出應有的貢獻。

從金字塔到三明治

作為敏捷在特定時期的產物,測試金字塔並不失其合理性,甚至還對自動化測試起到了重要推廣作用。但是,隨著行業整體技術能力的不斷提升,市場需求和競爭日趨激烈,在項目中具體實施測試金字塔時往往遭遇困難,即便藉助外力強推,其質量和效果也難以度量。

此外,隨著軟件設計和開發技術的不斷髮展,低層單元測試的傳統測試技術和落地,因前、後端技術棧的多樣化而大相徑庭;同時,在經歷過覆蓋率之爭,如何確保單元測試的規範和有效,也成為工程質量管理的一大挑戰;高層的端到端測試則基本不受技術棧頻繁更替的影響,隨著不同載體上driver類技術的不斷成熟,其開發複雜度反而逐漸降低。

這裡討論一種新的測試層級分配策略,我們稱之為三明治模型 。如下圖所示,該模型允許對不同測試層級的佔比進行動態調整,說明了倒金字塔形、沙漏形以及金字塔形分配對特定產品開發階段的積極作用。

測試三明治和雪鴞探索測試

(Sandwich)

產品開發的自動化測試策略

根據3X模型,在探索初期往往選擇避開自動化測試。一旦進入擴張期,產品的可伸縮性和行為一致性就成為共同目標,但此時也常會發生大的代碼重構甚至重寫,如果沿用測試金字塔,無論補充缺失的單元測試,還是隻對新模塊寫單元測試,都既損害了產品的快速伸縮能力,也無法保證面向用戶的產品行為一致性。因此,如果在探索後期先引入高層的端到端測試,覆蓋主要用戶旅程,那麼擴張期內所產生的一系列改動都能夠受到端到端測試的保障。

需要注意的是,用戶旅程在產品即將結束探索期時通常會趨於穩定,在擴張期出現顛覆性變化的概率會逐漸減少,端到端測試的增長率會逐步下降。

除此以外,隨著擴張期內不斷產生的模塊重構和服務化,團隊還應增加單元測試和集成測試的佔比。其中,單元測試應確保覆蓋分支場景(可以在CI中引入基於模塊的覆蓋率檢測);集成測試和某些團隊實踐的驗收測試,則需進一步覆蓋集成條件和驗收條件(在story sign-off和code review時驗收)。

許多新興的測試技術和工具擅長各自場景下的驗收測試,但更重要的仍是識別產品階段和當前需求,以滿足收益最大化。

測試三明治和雪鴞探索測試

(Sandwich-3x)

由此我們認為,隨著產品開發的演進,測試層級的分配應參考三明治模型,動態調整層級佔比,更加重視運營和市場反饋,致力於真正幫助產品走向成功。

端到端測試的機遇和挑戰

與其他測試層級相比,端到端測試技術的發展程度相對滯後。一方面,作為其基礎的driver工具要在相應載體成熟一段時間之後才能趨於穩定,web、mobile無不如是。另一方面,端到端測試偏向黑盒測試,更加側重描述用戶交互和業務功能,尋求硬核技術突破的難度較高,於是較少受開發人員青睞。但是,由於端到端測試更接近真實用戶,其在特定產品開發活動中的性價比較高,有一定的發展潛力。

然而,當前實踐中的端到端測試,普遍存在如下問題:

  • 低可維護性。一般實踐並不對測試代碼質量作特別要求,而這點在端到端測試就體現得更糟。因為其涉及數據、載體、交互、功能、參照(oracle)等遠比單元測試複雜的broad stack。雖然也有Page Object等模式的廣泛應用,但仍難以應對快速變化。

  • 低運行效率。如果拿單次端到端測試與單元測試相比,前者的運行效率肯定更低。因此只一味增加端到端測試肯定會損害構建-反饋循環,進而影響持續交付。

  • 高不確定性。同樣因為broad stack的問題,端到端測試有更高的幾率產生不確定測試,表現為測試結果呈隨機性成功/失敗,進一步降低運行效率,使得真正的問題很容易被掩蓋,團隊也逐漸喪失對端到端測試的信心。

  • 難以定位問題根因。端到端測試結果很難觸及代碼級別的錯誤,這就需要額外人工恢復測試環境並嘗試進行問題重現。其中所涉及的數據重建、用戶交互等會耗費可觀的成本。

方法

為了解決傳統端到端測試遇到的種種挑戰,本文設計了一種面向用戶旅程的端到端自動化測試框架——雪鴞(snowy_owl),通過用戶旅程優先、數據分離、業務複用和狀態持久化等方法,顯著提高了端到端測試的可維護性,降低不確定性的影響,並且能夠幫助團隊成員快速定位問題。

測試三明治和雪鴞探索測試

用戶旅程驅動

端到端測試應儘量貼近用戶,從用戶旅程出發能保證這一點。在雪鴞中,用戶旅程使用被稱作play books的若干yaml格式的文件進行組織,例如下列目錄結構:

測試三明治和雪鴞探索測試

其中每個play book由若干plots所組成,plot用於表示用戶旅程中的“情節”單位,其基本特徵如下:

  • 單一plot可以作為端到端測試獨立運行,例如發送一條tweet的情節:

測試三明治和雪鴞探索測試

  • 單一plot應是緊密關聯的一組用戶交互,並且具備體現一個較小業務價值的測試參照。

  • plot可以被play book引用任意次從而組成用戶旅程,play book同時定義了所引用plots之間的順序關係,基本語法如下所示:

測試三明治和雪鴞探索測試

其中plot_name表示情節的標題,digest和parent分別表示當前情節引用在整個端到端測試過程中的唯一標識和前序情節標識,初期開發人員可以通過各個情節的引用順序定義用戶旅程,大多數情況下digest和parent將由系統自動生成並維護。

整個play books集合將是一個以plots為基礎組成的森林結構,而端到端測試的執行順序則是針對其中每棵樹進行深度遍歷。

通用業務複用

由於plot本身必須是一個獨立可運行的端到端測試,那麼plots之間通常會共享一部分交互操作,例如用戶登錄。雪鴞允許把高度可複用的交互代碼進行二次抽取,稱作determination:

測試三明治和雪鴞探索測試

這樣,plot的代碼就可以簡化成:

測試三明治和雪鴞探索測試

這裡應注意Determination和Page Object的區別。看似使用Page Object可以達到相同的目的,但是後者與Page這一概念強綁定。而Determination更加側重描述業務本身,更符合對用戶旅程的描述,因此比Page Object在plot中更具適用性。當然,在描述更低層的組件交互時,Page Object仍然是最佳選擇。

測試數據分離

合理的數據設計對描繪用戶旅程非常重要,雪鴞對測試邏輯和數據進行了進一步分離。例如用戶基本數據(profile),同樣是使用yaml文件進行表示:

測試三明治和雪鴞探索測試

那麼在plot的實現中,就可以使用同名對象方法替代字面值:

測試三明治和雪鴞探索測試

情節狀態持久化

雪鴞的另一個重要功能是情節狀態的持久化和場景復原。為了啟用情節狀態持久化,開發人員需要自己實現一個持久化腳本,例如對當前數據庫進行dump,並按照雪鴞提供的持久化接口把dump文件存儲至指定位置。

當端到端測試運行每進入一個新的情節之前,系統會自動執行持久化腳本。也就是說,雪鴞支持保存每個情節的前置運行狀態。

當端到端測試需要從特定的情節重新開始運行時,雪鴞同樣會提供一個恢復接口,通過用戶自定義的數據恢復腳本把指定位置的dump文件恢復至當前系統。

該功能有兩處消費場景:

  • 由於broad stack的問題,端到端測試不確定性的技術因素一般較為複雜。實際經驗表明,測試的隨機失敗率越低,就越難以定位和修復問題,而通過不斷改進測試代碼的方式消除這種不確定性的成本較高,效果也不好。但是,可以儘量消除不確定性帶來的影響。例如,不確定測試導致的測試失敗,通常會導致額外人工驗證時間,完全可以選擇讓系統自動重試失敗的測試。另一方面,重試會造成測試運行效率降低,特別是針對端到端測試。當一輪端到端測試結束後,雪鴞只會自動重試失敗的情節測試,同時利用該情節對應的數據dump文件保證場景一致性,這就減少了重試整個端到端測試帶來的運行效率下降問題。

  • 當團隊成員發現端到端測試失敗,通常需要在本地復現該問題。而藉助測試dump文件,可以直接運行指定plot測試,從而避免額外的人工設置數據和交互操作,加快問題定位和解決。

實踐

雪鴞在筆者所在的項目有超過6個月的應用時間。該項目在產品開發方面長期陷入困境,例如過程中同時兼具了3X每個階段的特點,不僅缺少清晰的產品主線,還揹負了接棒遺留系統的包袱。這種狀況對工程質量管理提出了更大挑戰。

項目採用雪鴞對已有端到端測試進行了重構,生成了一個核心用戶旅程和三個涉及外部系統集成的重要用戶旅程,包含24個plots,9個determinations,使端到端測試實現了長期穩定運行。在本地相同軟硬件環境下,不確定性導致的隨機失敗從原有10%降低至1%以內,部署至雲環境並採用headless模式後,連續15天測試失敗記錄為零,運行效率的損失可以忽略不計。同時,當用戶旅程產生新分支時,可以引入新的情節測試節點,並且根據業務需求將其加入現有play book樹,從而實現端到端測試的快速維護。

持續集成與常態化運行

項目完整的端到端測試的平均運行時間保持在19分鐘左右,為了不影響現有持續集成節奏,CI每30分鐘自動更新代碼並運行端到端測試,結果在dashboard同步顯示,一旦發生測試失敗,第一優先級查找失敗原因並嘗試在本地復現和修復。

常態化運行端到端測試的另一個好處是,能夠以低成本的方式實現24小時監控系統各個組件的功能正確性,有助於更早發現問題:一次,產品即將上線的支付功能發生異常,查看CI記錄發現端到端測試在晚上9:15左右出現了首次告警。通過及時溝通,確認是海外團隊在當時擅自改動了支付網關的一個配置,造成服務不可用的問題,並迅速解決。

結論與展望

Kent Beck的3X模型,提出了從不同產品開發階段看待工程實踐的新視角。而敏捷一貫推崇的TDD等實踐,更多體現在個人技術專長(Expertise)方面,與產品是否成功並無必然聯繫。然而,程序員的專業主義(Professionalism)的確同時涵蓋了技術專長和產品成功兩個方面,二者相輔相成。因此,如何通過平衡眾多因素並最終提高整體專業性,這才是軟件工程面臨的經典問題。本文給出的測試三明治模型,目的就是幫助思考產品開發過程中測試層級間的平衡問題。

為了應對現有端到端測試面臨的挑戰,本文設計並實現了一種新的面向用戶旅程的端到端測試框架,通過職責隔離、業務複用和狀態持久化等手段,構建了易於維護且更加有效的端到端測試。同時,基於上述方法構建的測試代碼,更易於和自動化測試的其他研究領域相結合,在諸如測試數據構建、用例生成、隨機測試和測試參照增強等方向有進一步的應用潛力。


文/ThoughtWorks韓翼

原文鏈接:https://insights.thoughtworks.cn/new-test-model-and-frame/


分享到:


相關文章: