7

測試類型

參考附件。

位於象限底部的是 面向技術 的測試,也就是那些首先可以幫助到 開發人員 構建系統 的測試。 在這個象限內的測試,大部分都是可以自動化的。 EX:性能測試、單元測試。

相對的,位於頂部的測試,就是為了幫助非技術背景的人們,了解系統是如何工作的。 這種測試包括左上角的 大範圍、端到端 測試。 還有右上角 由用戶在 UAT 系統上,進行手工驗證的探索性測試。

在這個象限中,每個測試類型 都有自己的位置。 在不同系統中,每個測試類型的占比都會不同,但要知道自己有哪些選擇 ~ 放棄手工測試,盡量使用自動化測試是目前的趨勢。

手工測試是有用的,也有其必要性。但基於本章的內容,我們會主要 focus 在自動化測試的部分。

測試範圍

測試金字塔

(越往上、範圍更大、更有信心) 用戶介面測試 服務測試 單元測試 (越往下、執行速度更快、隔離的更好)

單元測試

單元測試通常只測試一個函數和方法呼叫。 通過 TDD (Test-Driven Design 測試驅動開發) 寫的測試就是屬於這一類。 由基於屬性的測試技術所產生的測試,也是屬於這一類。

單元測試是彼此獨立的,分別覆蓋一些小範圍的代碼。

這些測試的主要目的是對於功能是否正常 快速地給予反饋。 單元測試對於 重構 非常重要,因為我們知道,如果不小心犯了錯誤,這些小範圍的測試可以很快地給予提醒,讓我們可以放心地隨時調整代碼。

服務測試

服務測試是避開用戶介面,直接針對服務的測試。 在獨立的應用程序中,服務測試可能只測試 為用戶介面提供服務 的一些類。 對於包含多個服務的系統,一個服務測試只測試其中一個單獨服務。

只測試一個服務,是為了提高測試的隔離性,讓我們可以更快的定位到問題。

有些服務測試可能會執行的向單元測試一樣快。 但如果在測試中用了真實的資料庫,或是通過網路和外部進行合作,那麼測試的時間就會增加。

服務測試比單元測試的覆蓋範圍更大,因次當失敗時,也更難定位。 不過比起更大範圍的測試,服務測試中包含的部分已經很少了,因此也沒有那麼脆弱。

端到端測試

通常會覆蓋整個系統,涵蓋範圍最大,通常需要開一個瀏覽器去操作GUI的用戶介面。

這類測試會涵蓋大範圍的程式碼,倘若通過,會感覺十分愉悅 (誤) 但倘若失敗,要定位問題時,也會頭很痛 ((抱頭 Q___Q

權衡

我們要了解

越靠近金字塔頂端,測試覆蓋的範圍越大,而測試完也會越有信心,但需要花的時間也越久,所以反饋時間會拉長。

越靠近金字塔底部,測試會越快,反饋時間變短,也可以容易定位是哪裡被破壞了。這可以避免我們在 已經破壞某個功能 的狀態下,去執行下一個任務。但從另外一個角度,只測試底層的程式,並沒有足夠的信心整個系統都可以正常運作。

當單元測試以上的測試失敗以後,我們通常會寫一個單元測試去重現問題,以便將來可以用更快的速度去捕捉同樣的錯誤。 我們利用這樣的方式,來盡可能地縮短反饋週期。

比例

既然所有測試都有優缺點,那應該怎麼抓比例呢? 有一個經驗法則,是下一層的測試數量,應該比上面的一層多一個數量級。 如果當前的權衡確實給你帶來問題,那麼嘗試調整自動化測試的比例,這是重要的。

作者曾經在一個單塊系統上工作, 單元測試 4000 整合測試 1000 UI 測試 *60 而測試週期變得很長,主要是因為後兩者(尤其是後者)。 之後便盡量使用小範圍的測試來替換大範圍的測試。

有一個測試反模式,測試甜筒 / 倒金字塔 (真滴不要鬧了) 在這種模式,沒有小範圍,只有大範圍,測試 run 起來超慢,有問題超難找,真滴4不要鬧惹。

實現服務測試

服務測試 只是想測一個單獨的服務,為了隔離其他的相關服務

工廠模式 是所謂的打桩 Mock 是 Mock (雖然我還是不知道兩者的確實差異)

反正就是為了讓我們可以獨立測試要測的服務,將外部相依性拔掉的方式。 Mock 會進一步驗證請求本身是否被正確呼叫,但是過度使用 Mock 會導致測試變得脆弱。

如果可以,有個可以同時 打桩&Mock 的工具總是好的。 也有人把這兩者,統稱為 測試替身 (Test Double)。

微妙的端到端測試

介面展示的一個功能,往往涉及多個服務,所以進行 端到端測試 的時候需要部署多個服務。 顯然,這種測試可以覆蓋較大的範圍,也可以讓我們對系統更有信心。但另一方面,他也會消耗更多時間。失敗時的定位也更困難。

客戶服務    構建--單元測試--服務測試--端到端測試

但是我們需要問自己 當 端到端測試 需要部署其他服務時,我們應該使用哪一個版本的其他服務?

那其他服務的端到端測試,應該怎麼辦? 這樣會導致我們需要花費大量的成本在重複部署這些服務。

為了解決問題,有一種優雅的方式 讓多個流水線扇入 (fan in) 到一個獨立的端到端測試階段 (stage)

使用這種方法,任意一種服務的構建,到最後 都會觸發一次端到端測試

一些更好的 CI工具,可以很方便的地實現這樣的擅入模型。

端到端測試的缺點

很多

非常多

脆弱的測試

有時候失敗並不是因為功能真的被破壞了,而是因為一些其他原因。

可能我們要測的一個功能涉及了四五個服務 1. 而其中一個服務停止運行,都會導致測試失敗,但這種失敗跟功能本身沒有關係。 2. 一個臨時的網路故障也可能導致測試失敗,它同樣跟功能無關。 3. 涉及多線程功能的測試,因為資源競爭、超時,有時是功能真的被破壞了。

脆弱的測試是我們的敵人

因為這樣的失敗什麼都不能告訴我們,也沒有幫助。每個人都會期望重新構建一次,剛剛失敗的測試就會通過,而養成不好的習慣。

當發現脆弱的測試時,我們應該竭盡全力去解決這個問題。 否則人們會開始對測試套件失去信心,也就會演變成所謂的 異常正常化(the normalization of deviance) (隨著時間的推移,我們對事情出錯變得習以為常,並開始接受它是正常的)

當發現脆弱的測試時,應該立刻記錄下來。如果不能立即修復,就先移出套件,這樣就可以不受打擾的修復它。 1. 首先,先看能不能通過重寫來避免多線程 2. 再看看能否讓運行環境更加穩定 3. 更好的方法:能否用不容易出問題的小範圍測試 來取代脆弱的端到端測試。

有時候改變測試軟體本身,使之能夠更容易測試,也是正確的方向。

誰來寫這些測試?

一個比較合理的想法是,擁有這些服務的團隊來寫這些測試。 但倘若有一個服務涉及多個團隊,而端到端測試也被多個團隊共享時,誰該負責實現和維護這些測試呢?

不好的方式1

測試對所有人開放,所有人都可以隨意添加測試,甚至對測試質量沒有任何理解時。Boom!

不好的方式2

由一個專門的測試團隊來寫這些測試。 因為開發人員逐漸遠離測試程式,週期時間會變長,因為開發人員要等待測試團隊撰寫端到端測試。 因為測試程式由別的團隊來撰寫,開發人員很少參與,所以很難了解如何運行和修復。 但這種方式卻是一種很常見的組織模式....

有一種比較好的方式,是共享端到端測試的代碼權限,並同時對套件負責。所有的開發團隊,都必須為了套件的健康負責

測試多長時間

很少看到團隊精細的管理端到端測試、嘗試減少重複覆蓋的測試、花足夠的時間讓它變快。

運行緩慢、脆弱性 是很大的問題。 需要花一天甚至六個星期的測試 + 和功能無關的測試失敗,這真是大災難

當發現真的是功能被破壞時,都已經忘得差不多了 Q___Q

並行的去run 測試程式可以改善緩慢的問題,可以使用 Selenium Grid 來達到效果。但並不能改善重複覆蓋的問題。

刪除測試程式令人惶恐,可能有點類似移除機場的某些安保措施類似 =A= 移除不一定會被感謝,但是發生問題則肯定會被吊起來。 這需要更好的去理解風險,但人類並不擅長這點... 結果,就是很少能見到有人可以精細的對大範圍的、高負擔的測試進行管理和維護。

大量的堆積

端到端測試的反饋週期過長,不僅影響生產效率,也會使修復的週期拉長。

而大量的堆積,指得是在修復的這段時間,所累積的大量提交。這除了使修復更加困難,要部屬的內容也會越來越多。

解決這個問題的一個方法是,在解決問題的這段時間不準提交。 但這通常是不切實際的 ... 七個小時都不提交? 六週不提交?

*我想這也就是Git flow 裡面,為什麼會有 Release 線了。 累積的commit 會放在 Develop,準備要上線了,會開一個 Release 出來,執行端到端測試,測試完沒問題,就可以 Merge 回去 Develop 和 Master。 這時有一個小問題,感覺應該三種都要在 Feature 寫,這三個測試都要執行 Pass。 而在執行測試時,只執行自己的這些,不執行其他的。 那在Release 才執行全部的測試?

當部署變更的內容越多,發布的風險就越高,也就越可能破壞某些功能。 保障頻繁的發布,其實就是為了縮小發布的範圍。

元版本

在端到端測試階段,有可能會有一種謬誤:我知道所有服務在這個版本可以運作,何不一起部署呢? 為何不使用同一個版本號呢?

當修改、部署多個服務是可以接受、成為常態,很容易喪失微服務的主要優勢之一:獨立於其他服務 單獨部署一個服務的能力。

經常多個一起部署,經常會導致服務的耦合,本來分離的很好的服務,就會和其他糾纏的越來越緊密,這種耦合,會變得比使用單塊系統還要悲劇。

測試場景,而不是故事

這句話的意思是,端到端測試 並不需要把所有使用者案例、所有功能都測過一遍。當我們為每一個新添加的功能都增加一個端到端測試,我們會得到一個 臃腫的測試套件反饋週期很長巨大的重疊測試覆蓋率

我們只需要測試整個系統的核心場景,那些核心場景以外的功能,就放在服務測試就好。 這樣團隊之間需要就這些核心場景達成一致,並共同擁有和維護。 盡可能減少測試的數量 & 降低重複覆蓋的測試。

拯救消費者驅動的測試

CDC, Consumer-Driven Contract, 消費者驅動的契約

當使用CDC時,我們會定義消費者的期望,而這些期望會轉換成測試程式。 如果使用得到,CDC應該會成為 CI 流水線的一部分,這樣可以確保 如果契約被破壞,系統便無法部署。 更重要的,從測試的反饋週期來看,因為只需要針對開發人員執行這些CDC,所以他會比解決同樣問題的端到端測試更快也更可靠。

因為這些CDC 是對 服務 如何工作的期望,所以服務的下游依賴都可以使用 測試替身。雖然它們與測試金字塔中的服務測試都在同一層,帶側重的地方卻不一樣。 這些測試 是側重在 使用者 如何使用服務,而如果測試失敗的解決方式也會有所不同。如果CDC失敗了,消費者會有很明顯的影響。 這時候可以選擇修復問題,或是發起一個破壞性的討論。

所以通過CDC,我們在端到端測試之前,我們就有機會發現破壞性的變化。

Pact

某個消費者驅動的測試工具,支援Ruby、JVM、.NET。

開始的時候,Client 使用 Ruby DSL 來定義 except,Pact 的規範文件是 JSON 格式 (雖然也可以自己手寫,但使用語言的API會比較容易),同時提供一個 mock server,用來獨立測試 Client。

我們可以使用 JSON Pact 來 call 開發人員的API,根據 response 去驗證是否 PASS。 client 和 server 可以是不同的語言 ~

Pact 的 JSON 是由 Client 產生,該規範需要成為一個生產者可訪問的構建物。可以把這個構建物存儲在 CI/CD 工具的構建物倉庫中。 或是使用 Pact Broker,它可以讓你儲存 Pact 規範 的多個版本。

關於溝通

在敏捷中,故事通常被認為是一種促進溝通的方式。 CDC 也起到類似的作用。 它們可以推動關於 API 如何編寫的討論,當其被破壞時,也可以觸發該API該如何演進的討論。

CDC 需要 Client & Server 之間具有良好的溝通、信任。 如果雙方是同一團隊(甚至同一人)這應該不難,但如果你使用的服務是第三方溝通,那個CDC可能不適用,因為缺乏溝通和信任。 這種情況,可能就必須使用端到端測試。

還應該使用端到端測試嗎 OAO?

大部分的人更喜歡使用 CDC的工具 & 更好的監控 來代替端到端測試。

這代表要把端到端測試扔掉嗎? 不,他們會在使用一種叫做 語義監控 (semantic monitoring) 的技術,在監控生產系統時,用到 端到端測試。(ch.8)

在使用 CDC 和 良好的監控、部署技術 後,可以慢慢嘗試減少對 端到端測試 的依賴,直到完全不需要它們。

學習如何監控、修復生產環境,是很有價值的。

部署後再測試

大多數的測試會在部署前完成。 我們希望通過一系列的測試來證明在功能、非功能需求方面,系統的工作方式、行為都符合預期。 但我們必須承認,使用這種方法得到的收益會逐漸減少。 僅僅依靠部署之前的測試,我們不能將缺陷率降為零。

區分部署 / 上線

我們可以將軟體部署到生產環境,但在真正生產負載(production load) 之前 先運行測試。我們可以發現特定環境中的問題。

針對新部署軟件的一系列 冒煙測試套件,這些測試幫助我們識別環境有關的問題。 如果我們可以用一行指令來部署任何微服務,那麼我們也可以用一條指令來運行冒煙測試。

而另外一種方式,就是所謂的 藍 / 綠部署。 我們會擁有兩套生產環境,一套是原先運行的,一套是新部署上去的。測試完後,我們會將生產負荷切換到新部署的服務,但依然會保留舊版本的一段時間,倘若有狀況,就可以很快再切回來。

條件:能夠切換生產流量到不同主機上,例如可以透過更改DNS or 負載平衡的配置。

金絲雀發布

它和藍綠部署的差異在於:新舊版本共存的時間更長,並且經常會調整流量。

用來 觀察、比較 新舊版本的效益、運行狀況。

當使用金絲雀發布時,需要選擇是要 引導部分生產請求到金絲雀,還是複製一個請求。這方便大家對新舊版本做比較,因為請求一模一樣。 不過複製請求有可能很複雜,尤其在事件/請求 不是冪等的狀況。

金絲雀發布是一個功能強大的技術,同時對於推出一個糟糕的版本,提供工具來控制風險。 但是需要更多配置、占用更長時間的硬體、更複雜的請求路由。

平均修復時間 勝過 平均故障間隔時間

在Web的世界裡,這通常稱為 平均故障間隔時間 (Mean Time Between Failures, MTBF) 和 平均修復時間 (Mean Time To Repair, MTTR) 之間的權衡變化。

在 MTBG 和 MTTR 之外,還有別的權衡存在。 如果妳正試圖了解是否有人會真正使用我的軟體,那就需要盡快發布軟體。 這遠比構建健壯的軟體更有意義。 因為可以驗證之前的想法、業務模型是否能讓別人接受,在確認這件事情之前,測試並沒有那麼重要。

跨功能的測試

有一種測試也非常重要 跨功能性需求(Cross-Functional Requirement),是對 系統展現的一些特性 的概括術語。

  1. 網頁可接受延遲時間

  2. 系統能夠支持的客戶數量

  3. 用戶介面如何讓殘疾人使用

  4. 客戶數據安全

我們可以定義一些測試策略,來幫助我們朝著滿足目標的方向前進。 這些策略歸類為 屬性測試象限

對於一些CFR,可能希望能夠在追蹤單一一個服務,核心業務可以持久一點,相對邊緣的則允許更多的停機時間。這些權衡會對你如何設計你的系統,有比較大的影響。合適粒度的微服務,會給我們機會做這樣的平衡。

CFR 的測試也要遵循金字塔。 有一些測試需要端到端,EX:負載測試。 但其它的不需要,通常很容易使用更快的測試。 例如確保HTML標記 使用適當的可訪問特性,來通過無障礙。

建議盡早去看CFR

性能測試

與功能測試相同,性能測試也可以是各種範圍的混合。你可能決定想測試單個獨立服務的功能,可以大量併發測試 系統中核心場景的端到端場景 性能。

為了產生有價值的結果,通常需要模擬客戶逐漸增多,呼叫延遲 隨著負荷的增加而變化,所以性能測試通持需要持續一段時間 ~

因為性能測試要運行很長的時間,有一種做法是:每天運行一個子集合,每周運行一個大的集合。不管選擇什麼方法,都要確保可以盡可能頻繁的運行。如果只要通過查看少量的提交,就能找到問題,不是好棒棒嗎 XD

運行完一定要看結果

運行完一定要看結果

運行完一定要看結果

很多團隊建立了性能測試,但運行後卻不看結果。 =A= 性能測試需要有目標,有了目標之後,基於運行結果去讓構建變成綠色或紅色,紅色就是一個清晰的信號。

結論

  1. 優化快速反饋,並相應的使用不同類型的測試。

  2. 盡可能使用 CBC 來替換 端到端測試。

  3. 使用 CBC 提供團隊之間的對話重點。

  4. 嘗試理解 投入更多的努力測試更快地在生產環境發現問題 之間的權衡(MTBF 與 MTTR 權衡的優化)

<敏捷軟體測試> - Lisa Crispin & Janet Gregory

Last updated