測試是最普遍的品質改善活動。本章聚焦於開發者測試(developer testing)——單元測試、元件測試與整合測試,屬於白箱測試(white-box testing)。測試的目的是偵測錯誤而非證明程式正確;除錯(診斷與修正根因)留待下一章討論。

22.1 開發者測試在軟體品質中的角色#

測試是品質計畫的重要環節,但不應是唯一環節。協同開發實踐(如審查)能找到更高比例的錯誤,且每個錯誤的成本不到測試的一半。個別測試步驟的缺陷偵測率通常低於 50%,組合後也常低於 60%。

測試對開發者而言是反直覺的:目標是「破壞」軟體;無法證明無錯;測試本身不改善品質(如同體重計不能幫你減肥);需假設程式碼有錯——Myers 的實驗中,經驗豐富的程式設計師平均只找到 15 個已知缺陷中的 5 個。

開發者測試應佔專案總時間的 8-25%。測試結果可評估可靠度、導引修正方向,以及長期追蹤常見錯誤類型。

22.2 開發者測試的推薦方法#

方法說明
針對需求測試確保每項需求都有對應測試案例,盡早規劃
針對設計測試確保設計已被正確實作
使用基礎路徑測試與資料流測試至少測試每一行程式碼
使用過往錯誤清單根據歷史錯誤類型設計測試案例

先寫測試(test-first)是過去十年最有價值的實踐之一:不增加工作量(只是重新排序)、更早偵測缺陷、迫使先思考需求與設計、及早暴露需求問題。

開發者測試的侷限:偏好乾淨測試而非髒測試(成熟組織髒測試比例為 5:1);高估覆蓋率(自認 95%,實際 50-60%);多數人以語句覆蓋為滿足,但分支覆蓋才是更好的標準。

22.3 測試技巧錦囊#

窮舉測試不可能——即使簡單的姓名+地址+電話就有約 10^66 種組合。測試的藝術在於挑選最可能揭露錯誤的少數案例。

結構化基礎測試(Structured Basis Testing)#

計算最少測試案例數:從 1 開始,每遇到 ifwhileforandor 各加 1,case 每分支加 1(無 default 再加 1)。這只確保所有程式碼被執行,無法涵蓋資料變化。

資料流測試(Data-Flow Testing)#

資料存在三種狀態:已定義(Defined,初始化未使用)、已使用(Used)、已終結(Killed,如指標被 free)。

應懷疑的異常資料狀態組合
  • 定義-定義——連續定義兩次,浪費或錯誤
  • 定義-離開——區域變數定義後未使用就離開常式
  • 定義-終結——定義後直接終結,多餘變數或遺漏使用
  • 進入-終結 / 進入-使用——區域變數未定義就被終結或使用
  • 終結-終結——雙重終結,對指標尤其致命
  • 終結-使用——使用已終結的變數是邏輯錯誤

關鍵是測試所有定義-使用路徑。好的策略:先做基礎測試,再補遺漏的定義-使用組合。

等價分割(Equivalence Partitioning)#

將輸入空間分成等價類別,每類只需一個代表值。在基礎路徑與資料流測試後額外洞見有限,但從外部規格出發或面對複雜資料時特別有用。

錯誤猜測與邊界分析#

錯誤猜測(Error Guessing)根據直覺與過往經驗設計測試。邊界分析(Boundary Analysis)針對差一錯誤,每個範圍條件測試三個案例:邊界以下、邊界上、邊界以上。複合邊界則測試多變數同時取極端值的情境。

不良資料與良好資料的類別

不良資料:過少/無資料、過多、錯誤類型、錯誤大小、未初始化。

良好資料:一般案例(中間值)、最小正常組態(如空白試算表)、最大正常組態(產品標示上限)、舊資料相容性(回歸測試基礎)。

選擇容易手算的整數值(如 $20,000)作為測試資料,避免醜數字(如 $90,783.82)。同一等價類別中整數值不會少發現錯誤,但能降低手算出錯的風險。

22.4 典型錯誤#

缺陷分布極度不均:80% 的錯誤集中在 20% 的類別中,50% 集中在 5% 的類別中。IBM IMS 系統修復 425 個類別中的 31 個易出錯類別後,客戶缺陷減少十倍、維護成本降 45%。

維護策略的啟示:應集中識別、重新設計並重寫易出錯的常式,而非在原程式碼上反覆修補。

錯誤特徵:85% 只需修改一個常式;95% 是程式設計師的錯;36% 的建構錯誤是書寫失誤(三個史上最昂貴的軟體錯誤都是修改了單一字元);16-19% 源於誤解設計;85% 在數小時內可修。

建構缺陷佔比:小型專案約 75%,大型專案至少 35%(有些報告高達 75%)。預期缺陷數:業界平均 1-25 個/千行,Microsoft 發布產品 0.5 個/千行,Cleanroom 發布產品 0.1 個/千行,TSP 約 0.06 個/千行。

測試案例的錯誤密度常等於甚至高於被測程式碼。應以開發正式程式碼的態度來開發測試案例:逐行檢查、盡早規劃、保存以供回歸測試、整合到測試框架中。

22.5 測試支援工具#

工具說明
測試鷹架(Scaffolding)模擬物件/樁常式(mock/stub)替代依賴項;驅動程式(driver/test harness)呼叫被測物件;假檔案(dummy file)提供已知無誤的小型測試檔案。可用 JUnit、CppUnit、NUnit 等框架
差異比較工具(Diff)比較新輸出與預期輸出,最簡單的回歸測試方法
測試資料產生器隨機產生異常組合,可加權以強調常見輸入範圍
覆蓋率監控器未量測覆蓋率的測試通常只執行 50-60% 程式碼
資料記錄器與日誌記錄系統狀態,類似飛機黑盒子
符號式除錯器逐行步進觀察變數值,也適合學習語言與編譯器最佳化
系統擾動器記憶體填充(暴露未初始化變數)、記憶體搖晃(偵測絕對位址依賴)、選擇性記憶體失敗、邊界檢查
錯誤資料庫追蹤重複錯誤、偵測/修正速率、狀態與嚴重程度

22.6 改善測試程序#

規劃測試——從專案初期就將測試提升到與設計和編碼同等的重要性;有計畫才能可重複,可重複才能改善。

回歸測試——修改後重新執行先前的測試以確保未引入新缺陷,舊測試必須保留。

自動化測試——回歸測試的唯一實際做法。手動測試的錯誤率與被測程式碼的缺陷率相當,約只有一半被正確執行。自動化的好處:更不易出錯、可反覆使用、支援頻繁測試實踐、盡早偵測問題、為大規模修改提供安全網。

22.7 保留測試記錄#

缺陷記錄應包含的項目
  • 管理資訊(回報日期、回報者、標題、建置編號、修正日期)
  • 完整問題描述與重現步驟
  • 建議的暫時解法與相關缺陷
  • 嚴重程度(致命、困擾、外觀)
  • 來源(需求、設計、編碼、測試)與編碼子分類
  • 受影響的類別/常式、程式碼行數
  • 發現與修正所需時間

可計算的指標:各類別的缺陷數、每缺陷平均測試時間、每測試案例平均缺陷數、覆蓋率、各嚴重等級的未結缺陷數。個人也可維護常見錯誤清單與時間記錄。

更多資源#

  • Kaner, Falk, Nguyen, Testing Computer Software, 2nd ed. (1999)——軟體測試全面指南
  • Whittaker, How to Break Software (2002)——23 種攻擊手法讓軟體失敗
  • Myers, The Art of Software Testing (1979)——經典著作
  • Beck, Test-Driven Development: By Example (2003)——先寫測試的完整範例

要點#

  • 開發者測試是完整測試策略的關鍵環節,但獨立測試同樣重要
  • 先寫測試案例能縮短缺陷偵測-除錯-修正的循環,且不增加額外時間
  • 測試只是品質計畫的一部分;高品質的開發方法和協同開發實踐至少同等重要
  • 可透過基礎路徑測試、資料流分析、邊界分析、不良/良好資料類別系統性地產生測試案例,再用錯誤猜測補充
  • 錯誤集中在少數易出錯的類別與常式——找出它們、重新設計、重新撰寫
  • 測試資料的錯誤密度常高於被測程式碼,應以同等嚴謹態度開發測試案例
  • 自動化測試對回歸測試不可或缺
  • 長期改善測試的最佳方式是使流程規律化、量測並據此調整