《持續交付:發佈可靠軟件的系統方法》第4章 測試策略的實現

  • 2019 年 10 月 8 日
  • 筆記

第4章 測試策略的實現

4.1 引言

  • 戴明14條之一就是:「停止依賴於大批量檢查來保證質量的做法。改進過程,從一開始就將質量內嵌於產品之中。」[9YhQXz]測試是跨職能部門的活動,是整個團隊的責任,應該從項目一開始就一直做測試
  • 質量內嵌是指從多個層次(單元、組件和驗收)上寫自動化測試,並將其作為部署流水線的一部分來執行,即每次應用程序的代碼、配置或環境以及運行時所需軟件發生變化時,都要執行一次
  • 質量內嵌還意味着,你要不斷地改進自動化測試策略
  • 這些測試不僅僅對系統進行功能測試。容量、安全性及其他非功能測試也應儘早建立,也應該為它們寫自動化測試套件。這些自動化測試確保不符合需求的問題能儘早暴露,降低其修復成本
  • 如果在項目開始時就遵從適當的紀律準則,這種理想國是完全可以實現的。假如項目已經進行了一段時間,然後你才想實現這樣的理想國,就有點兒困難了

4.2 測試的分類

  • Brian Marick提出了如圖所示的測試象限,它被廣泛地應用於對為了確保交付高質量應用軟件而做的各種類型的測試的建模

4.2.1 業務導向且支持開發過程的測試

  • 這一象限的測試通常稱作功能測試或驗收測試。驗收測試確保用戶故事的驗收條件得到滿足。在開發一個用戶故事之前,就應該寫好驗收測試,採取完美的自動化形式。驗收測試可以測試系統特性的方方面面,包括其功能(functionality)、容量(capacity)、易用性(usability)、安全性(security)、可變性(modifiability)和可用性(availability)等
  • 時新的自動化功能測試工具,比如 Cucumber、JBehave、Concordion以及Twist,都旨在把測試腳本與實現分離,
  • 等價劃分分析(equivalence partitioning analysis)和邊界值分析可以幫助你得到儘可能小的用例集合,並保證測試覆蓋完整的需求。然而,即便如此,你也要憑直覺來挑選一些最為相關的用例

自動化驗收測試

  • 自動化驗收測試有很多很有價值的特性
    • 它加快了反饋速度
    • 它減少了測試人員的工作負荷
    • 它讓測試人員集中精力做探索性測試和高價值的活動,
    • 這些驗收測試也是一組回歸測試套件
    • 就像行為驅動開發(BDD)所建議的那樣,使用人類可讀的測試以及測試套件名,我們就可以從這些測試中自動生成需求說明文檔
  • 一般我們將代碼覆蓋率高於80%的測試視為「全面的」測試,但測試質量也非常重要,單單使用覆蓋率這一指標是不夠的
  • 作為對自動化驗收測試覆蓋率比較好的一種評估方法,假設要替換系統中的某一部分(比如持久層,使用另一種實現來替換它)。當你完成替換時,運行了自動化測試,並且測試全部通過了。你有多大自信心,認為系統可以正常運行呢?

4.2.2 技術導向且支持開發過程的測試

  • 有三種測試屬於這一分類:單元測試、組件測試和部署測試。單元測試用於單獨測試一個特定的代碼段。因此,單元測試常常依賴於用測試替身(test double)模擬系統其他部分
  • 然而,為了獲得高速度,也有一些代價,即可能會錯過應用系統不同部分之間交互時產生的一些缺陷
  • 組件測試用於測試更大的功能集合,因此可能會捕獲這類問題

4.2.3 業務導向且評價項目的測試

  • 這類手工測試可以驗證我們實際交付給用戶的應用軟件是否符合其期望
  • 一種非常重要的面向業務且評價項目的測試是演示。在每個迭代結束時敏捷開發團隊都向用戶演示其開發完成的新功能。在開發過程中,我們也應該儘可能頻繁地向客戶演示功能,以確保儘早發現對需求規範的錯誤理解或有問題的需求規範

4.2.4 技術導向且評價項目的測試

  • 驗收測試分為兩類:功能測試和非功能測試。非功能測試是指除功能之外的系統其他方面的質量,比如容量、可用性、安全性等
  • 很多項目並不把非功能需求放在與功能需求同等重要的地位來對待,而且可能會更糟糕,他們根本不去驗證這些非功能需求。雖然用戶很少花時間提前對容量和安全性做要求,但一旦他們的信用卡信息被盜,或者網站由於容量問題總是停止運行,他們就會非常生氣

4.2.5 測試替身

  • 自動化測試的一個關鍵是在運行時用一個模擬對象來代替系統中的一部分。這樣,應用程序中被測試的那部分與系統其他部分之間的交互可以被嚴格地掌控,從而更容易確定應用程序中這一特定部分的行為。這樣的模擬對象常常就是mock、stub和dummy等
  • 術語「測試替身」(test double),並進一步區分了各種測試替身
    • 啞對象(dummy object)是指那些被傳遞但不被真正使用的對象。通常這些啞對象只是用於添充參數列表
    • 假對象(fake object)是可以真正使用的實現,但是通常會利用一些捷徑,所以不適合在生產環境中使用。一個很好的例子是內存數據庫
    • 樁(stub)是在測試中為每個調用提供一個封裝好的響應,它通常不會對測試之外的請求進行響應,只用於測試
    • spy是一種可記錄一些關於它們如何被調用的信息的樁。這種形式的樁可能是記錄它發出去了多少個消息的一個電子郵件服務
    • 模擬對象(mock)是一種在編程時就設定了它預期要接收的調用。如果收到了未預期的調用,它們會拋出異常,並且還會在驗證時被檢查是否收到了它們所預期的所有調用

4.3 現實中的情況與應對策略

4.3.1 新項目

  • 在這種情況下,最重要的事情就是一開始就要寫自動化驗收測試。為了能做到這一點,你需要:
    • 選擇技術平台和測試工具
    • 建立一個簡單的自動化構建
    • 制定遵守INVEST原則[即獨立的(Independent)、可協商的(Negotiable)、有價值的(Valuable)、可估計的(Estimable)、小的(Small)且可測試的(Testable)]的用戶故事[ddVMFH]及考慮其驗收條件
  • 然後就可以嚴格遵守下面的流程:
    • 客戶、分析師和測試人員定義驗收條件
    • 測試人員和開發人員一起基於驗收條件實現驗收測試的自動化
    • 開發人員編碼來滿足驗收條件
    • 只要有自動化測試失敗,無論是單元測試、組件測試還是驗收測試,開發人員都應該把它定為高優先級並修復它
  • 從一開始,測試人員就應該參與需求寫作的過程,並確保在整個系統演進的過程中,他們都能為具有一致性的和可維護的自動化驗收測試套件提供支持

4.3.2 項目進行中

  • 引入自動化測試最好的方式是選擇應用程序中那些最常見、最重要且高價值的用例為起點。這就需要與客戶溝通,以便清楚地識別真正的業務價值是什麼,然後使用測試來做回歸,以防止功能被破壞

4.3.3 遺留系統

  • 沒有自動化測試的系統就是遺留系統。雖然對這個定義還有一些爭議,但它的確有用而且簡單
  • 通常比較有效的策略是在測試結束後仔細地驗證系統的狀態。如果時間來得及,你可以再測試一下這個用戶故事的Alternate Path。最後,你還可以寫更多的驗收測試來檢查一些異常條件,或防禦一些常見的失效模式(failure mode),或防止不良的副作用
  • 切記,只寫那些有價值的自動化測試就行。如果只是增加新功能,而不需要修改這個提供支撐的框架代碼時,為這部分代碼寫全面的測試是沒有什麼價值的

4.3.4 集成測試

  • 我們所說的「集成測試」是指那些確保系統的每個獨立部分都能夠正確作用於其依賴的那些服務的測試
  • 黑盒測試是價值最高的測試,並要列出外部系統所有可能的響應,並為每個響應寫測試。這個模擬的外部系統需要找到某種方式識別你的請求,並回發正確的響應,假如有個請求不能被外部系統所識別,則應該返回一個異常

4.4 流程

  • 最好的解決方案就是在每個迭代開始時,召集所有的項目干係人開個會。假如沒有做迭代式開發,那麼就在某個用戶故事開始開發的前一周召開這樣的會議。讓客戶、分析人員、測試人員坐在一起,找到最高優先級的測試場景
  • 另一種方法是為測試創立一種DSL(Domain-Specific Language,領域專屬語言),並用這種DSL來書寫驗收條件。我們最起碼要讓客戶當場找出最簡單的驗收測試場景,並覆蓋這些場景的Happy Path

管理待修復缺陷列表

  • 達到這一目標的一個方法是,無論什麼時候,一旦發現缺陷就立即修復它
  • 還一種處理缺陷的方法,那就是像對待功能特性一樣來對待缺陷
  • 我們可以把缺陷分為嚴重(critical)、阻塞(blocker)、中(medium)和低(low)四個級別

4.5 小結

  • 在很多項目中,測試被認為是一個由一些專職人員負責的獨立階段。可是,只有當測試成為與軟件交付相關的每個人的責任,並從項目一開始就被引入並持續進行時,才能產生高質量的軟件

  • 《Agile Testing》
  • 《敏捷開發的藝術》
  • 《xUnit Test Patterns》
  • 《Release It!》

工具

  • 自動化功能測試工具,比如 Cucumber、JBehave、Concordion以及Twist