想做測試工程師,這7件事你必須先知道

摘要:寫代碼歸開發攻城獅,測試歸測試攻城獅,大部分情況下雙方處於“紅藍對峙”狀態。

一、“開發者測試” 就是“開發者來測試”

開發者測試是現代軟件工程中非常重要的一環,敏捷開發、主幹開發這些先進的項目管理方法和流程都基於完善的開發者測試。當每個月甚至每週都要交付一個版本時,不可能投入大量的測試工程師來進行大規模的系統級別測試,這時候就需要把整個測試金字塔中的絕大部分測試通過自動化來完成。

我們今天談開發者測試,什麼是“開發者測試”? 我司有清晰的開發與測試之分。寫代碼歸開發攻城獅,測試歸測試攻城獅,大部分情況下雙方處於“紅藍對峙”狀態。這與我10多年前的研發團隊狀況非常相似。而現在的軟件工程,專職的“測試攻城獅”非常少,很多公司開發測試比例大於10:1,甚至一些部門沒有測試攻城獅一說。 而測試攻城獅的角色不再是手動跑測試用例的“苦力”,而是管理產品的測試系統,對產品測試進行規劃、分析歸納功能測試思維導圖、設計測試用例及帶領研發團隊進行測試工作,更像一位“測試專家/測試教練”。舉個簡單的例子,我之前做的產品是在線視頻會議協作產品。我們每天的線上例會就是用自己做的產品,而且會使用自己開發的新功能測試站點來開“站會”。除了花少量的時間做dialy update,然後就是測試專家帶領團隊(包括PO、架構師、SM、Dev)按照計劃來進行集中(半個小時)的測試。所以說“開發者測試”就是“開發者來測試”,而我們傳統的衆多測試工程師面臨三種出路:成長、轉型、淘汰。“測試專家”在項目中的話語權也很高,我之前的公司使用主幹開發,有個“一進一出”的評審,團隊的這種類型的“測試專家”有一票否決權。甚至在公司有PE級別的測試專家(相當於我司20-21級的技術專家)。

  • 一進:對於一個功能是否能夠進入release branch,在release branch打開feature toggle進行發佈級別的測試。
  • 一出:在engineer release時,該功能質量合格,允許feature toggle進入產線。

二、沒有什麼測試不可以“自動化測試”

回到測試金字塔,從測試的"開發成本"、“執行成本”、“測試覆蓋率”、“問題定位”四個維度來看,基於代碼級別的白盒測試是及其重要的。

  • 開發成本: 實現測試用例的成本。
  • 執行成本:運行一次測試用例的成本。
  • 測試覆蓋率:我們通常所說的line coverage和branch coverage
  • 問題定位:測試出現問題,定位問題的效率

通過測試金字塔及其四個測試維度評估,我們可以得出:

  • 儘可能地多做Low Level Test :因爲他們的執行速度相較於上層的幾個測試類型來說快很多且相對穩定,可以一天多次執行。一般來說,LLT灰做到持續集成構建任務中去,甚至在MR中執行,保障進入代碼倉庫的代碼質量。
  • 在自動化保障的情況下,執行一定規模的IT、ST、UI Test:因爲他們的執行速度較慢,環境依賴較多,測試先對不穩定。通常在夜裏執行一次,階段性的檢查代碼質量,反饋代碼問題。
  • 儘可能地少做大規模的手動測試:因爲他們的執行速度相較LLT且不夠穩定,人力成本較高,也無法做到一天多次執行,每次執行都要等很久才能獲得反饋結果。但是,他們更貼近真實用戶場景,所以要確保一定週期內或者關鍵節點時間執行以下這幾個測試以確保軟件質量。

現在很多公司已經迭代發佈的週期越來越短,甚至做到了2周。手動測試顯然無法適應這種開發模式,而把手動測試的用例通過各種技術方案自動化是唯一途徑。代碼層面,從底層業務代碼到UI代碼,只要架構設計合理,都是可以做UT。最頂層的UI交互測試,測試用例也是可以自動化運行(大部分UI框架都可以通過accessibility的接口進行UI自動化測試),試想連咱們手機硬件都可以自動化測試“摔手機”這種極端測試,軟件有啥做不到?至少有些業界技術大牛公司的某些產品,從代碼提交Merge Request,到產品上產線是可以以天來計算的。這種產品的測試是不會也不可能通過手工測試來完成的。

三、開發者測試”利在當下“,”贏得未來“

很多人都認爲底層的開發者測試,花了大量的時間,寫了大量的代碼,然後來保證功能的正確性,但是每次代碼功能或者結構的的變更都要修改測試代碼。我手動調試和驗證效率更高。的確通過UT,API測試來調試代碼與自己手動運行調試區別不大,但是通過開發者測試對代碼進行調試,從而保證當前項目迭代的質量;但是其更重要的作用不是這個。

我們在bug分類中有這樣一些名詞 : Build Regression Bug, Release Regression Bug。

  • Build Regression Bug : 開發中同樣的功能在新版本出現一個bug,但是在之前的版本沒有這個問題,我們叫做Build Regression Bug.
  • Release Regression Bug : 產線上同樣的功能在新版本出現一個bug,但是在之前的版本沒有這個問題,我們叫做Release Regression Bug.

我們每次commit到產品中的代碼,沒有人可以保證其100%不會出現問題,在敏捷開發的這種快速迭代下,不太可能進行全功能的手動測試,所以開發者測試,特別是底層的UT、API測試、集成測試,能夠很容易的識別發現這類問題。所以說開發者測試”利在當下“,”贏得未來“。

四、TDD不是必須先寫測試代碼

對於TDD,大家的認知是先寫測試代碼,再在寫實現代碼,這個說法對也不對。概念上沒錯,但是如果嚴格這樣做,效率未必最高,這也是TDD很難推廣的原因之一。我們把編碼實現分成3個部分:實現代碼、測試代碼、調試代碼。按照TDD的概念時先寫測試代碼、然後編碼,最後調試。我們通常在代碼實現時,一開始不大可能考慮的非常清晰,把接口定義的完全準確,如果嚴格按照測試、編碼、調試來做,測試代碼要隨着編碼頻繁修改。 當然這本身不是什麼大問題,在實際執行過程中,很多人習慣先搭好代碼框架、測試框架,然後在編碼,測試。等測試完成後在進行調試。所以從華爲灰度管理的角度上來說,只要單元測試在調試之前,都可以稱作TDD開發模式。BTW,當然現在開始流行BDD,這裏想說的是如果連我說的TDD都做不到的團隊,就不要考慮BDD了。

(Behavior-Driven Development:BDD將TDD的一般技術和原理與領域驅動設計(DDD)的想法相結合。 BDD是一個設計活動,您可以根據預期行爲逐步構建功能塊。 BDD的重點是軟件開發過程中使用的語言和交互。 行爲驅動的開發人員使用他們的母語與領域驅動設計的語言相結合來描述他們的代碼的目的和好處。 使用BDD的團隊應該能夠以用戶故事的形式提供大量的“功能文檔”,並增加可執行場景或示例。 BDD通常有助於領域專家理解實現而不是暴露代碼級別測試。它通常以GWT格式定義:GIVEN WHEN&THEN。)

五、UT覆蓋率100%真的很不好


於單元測試,我們都會關注一個指標“覆蓋率”。不管模塊、函數、行、分支覆蓋率,必須要有一定比例的覆蓋率。但是每一項你都做到了100%,那麼會給你打“差評”。不是說你做到不好(這裏不談是不是用了正確的方式),而是成本和性價比問題。以最難達到的分支覆蓋率(branch coverage),如果要做到100%的覆蓋率,有些內存分配或者容錯保護的分支都必須測試到,那麼你的測試用例考慮要翻倍,但是並沒有帶來的相應價值。甚至一些代碼條件分支在程序運行的生命週期內都沒有被執行過。

  • 模塊覆蓋率:業務模塊代碼通過UT,架構模塊代碼通過IT;就從UT的覆蓋率的角度上去看,不需要去測試架構代碼。
  • 函數覆蓋率:不要爲一些無任何邏輯的代碼去寫UT。比如我們有些函數就是get/set一個屬性,內部實現就用一個變量來賦值保存。這種函數寫UT就是爲了覆蓋率而寫,沒有任何真正的意義。
  • 行覆蓋率:通常來看平局80%上下的行覆蓋率是一個合理的指標,有些可以爲0%,而有些需要100%,如果全部代碼都超過90%,其成本較高,效率較低,不建議這樣做。
  • 分支覆蓋率:越複雜的業務邏輯,越要寫更多的測試用例來覆蓋,而一些內存分配出錯邏輯判斷可以不需要測試。

六、用測試來驅動架構和代碼質量

這裏談測試驅動架構和代碼質量,主要說的是讓代碼具備完善的可測試性,什麼是代碼的可測試性?簡單的說就是類與類之間,模塊與模塊關係解耦,類與類,模塊與模塊通過接口編程。依賴的接口通過被動注入式傳入,而不是主動獲取式。對於程序正常運行時,所傳入的接口參數是真實的業務對象,而做測試時,可以傳入fake的模擬實現。當然不是所有的依賴模塊都這樣做,一些與業務無關的Utility Library,或者一些特定的數據對象實現,可以直接調用。

這裏我們講到了fake與mock,關於Test Doubles,基本上的概念如下,具體每種代表什麼意義,大家可以自行上網搜索

  • 虛擬對象(dummy)
  • 存根(stub)
  • 間諜(spy)
  • 模擬對象(mock)
  • 僞對象(fake)

當前我司大家在做開發者測試時,基本上都在用Mock Object(實際上在用的過程中,很多是在用入參返回值控制的Stub)。拋開概念上的問題,雖然通過Mock的方式也是可以測試代碼,但是實際上用Mock基本上意味着我們的代碼關聯性較強,模塊顯示依賴較重,模塊移植性較差,特別是C語言編程這種問題特別多。以至於現在很多模塊根本無法開展單元測試,更多的是在做集成測試。

爲什麼會出現這種情況? 我們的高級別的架構師更多的在考慮系統級別的架構設計,把系統模塊,各個應用之間的關係梳理的非常清晰,通常情況下,高級別的架構師可以把系統模塊或應用之間的關係設計的較爲合理。然而到了具體的應用業務內部的設計與實現,交給了低級別的架構師來完成。實際上這些模塊內部的代碼量並不小,很多都是幾十萬行甚至上百萬行的代碼量。這時候架構師的水平決定了代碼的Clean Code質量。我司目前代碼上的問題很多不是系統架構的問題,而是具體業務實現中,缺少嚴格的要求和合理的架構設計。如果在應用級別有一套架構方案來規範,那麼至少在模塊的接口以及模塊與模塊之前的交互上也能達到和系統設計一樣較爲清晰合理。那麼不確定的部分就時每個子模塊內部幾千上萬行的代碼部分。

之所以提出用測試驅動架構和代碼質量,當給測試提出一個很高的標準時,大家不得不從架構上去解決測試的問題,當測試的問題解決時,Clean Code L3自然而然就達到了。

七、從“我要寫測試依賴代碼”到“我要寫測試依賴代碼”

這句話看着很奇怪,實際上是從根本上去解決單元測試的根本方法。 模塊之間有依賴,不管是通過Mock還是Fake的方法,不管架構上如何合理,這種依賴是不能消除的,我們做到更多的是合理的設計讓依賴與模塊解耦。第一個“我要寫測試依賴代碼”,指的是當我實現我的模塊時,我要寫測試代碼來測試。而然我要考的是如何寫我的測試依賴。而第二個“我要寫測試依賴代碼”指的是,當我實現我的代碼時,我要考慮的是依賴我的模塊在測試時,對於我的依賴該怎麼解決,"我要寫測試依賴代碼”(就是我說的fake對象與實現)來幫助依賴我的模塊解決測試依賴問題。

  • 思維轉變、測試驅動:開發一個模塊,不要先考慮怎麼測試自己,先考慮如果別人依賴我,我該怎麼讓別人更容易測試。模塊的提供者,不止要提供模塊代碼,還要提供一個可複用的Faked對象(調用驗證;返回值;參數驗證;參數處理;功能模擬等)。
  • 模塊代碼的編寫者實現自己的Fake實現,基本上大部分的代碼是由模塊編寫者來完成,同時這是一個可複用的Fake實現。模塊依賴方根據自己一些特殊的業務需求來添加自己的代碼。基本上遵循80/20原則。
  • 架構上依賴解耦,通過注入依賴的方式進行接口編程。開發者測試使用Fake來實現依賴。
  • 當編寫測試代碼時,所有依賴的接口、依賴的實現都基本完成,更多的關注些測試用例而不是測試依賴。

 

點擊關注,第一時間瞭解華爲雲新鮮技術~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章