軟體測試是sampling的活動
我想你一定聽過很多人說, 測試應該要涵蓋所有狀況, 或是抱怨未甚麼連簡單的東西都沒有測試到, 或者測試為什麼需要這麼多時間....
這是因為他們對測試這個活動的本質有點誤解, 他們不知道軟體測試是sampling的活動. 即然是取樣, 自然不會涵蓋所有狀況; 有可能你選樣不好, 導致某些狀況漏掉; 或者是隨著你取樣的多寡, 自然需要花的時間也就不同.
這時候你會問, 為什麼軟體測試是sampling的活動?
這個問題的答案是, 因為可以測試的組合是無限多種, 你不可能有無限的時間去做測試, 因此你必須挑選一些有代表性的來測試, 希望他能夠涵蓋大部分的狀況, 讓你投資較小的資源, 得到最大的效益.
這時候你又問到, 為什麼測試的組合是無限多種呢?
這是我們試想有一個程式, 當你按空白鍵時, 會顯示圖案在螢幕上, 按其他按鍵則不會顯示任何東西.
當你使用黑箱測試的方法, 也就是在不知道程式內容狀況下測試, 你會如何進行一個完整的測試?
把所有按鍵都按一次, 看看是否照預期的結果運作. 這樣就好了嗎?
程式設計師若是設定連按八次return鍵也出現同樣的效果, 你怎麼會知道呢? 若是要防止這樣的事情, 你要試多少種組合才能發現? 答案可能無限次.
你會說不可能有能寫這樣的程式, 那你說微軟復活節彩蛋的程式是怎麼出來的? 那些銀行後門程式又是怎麼來的?
那你會說如果可以做白箱測試就可以避免這樣的狀況. 是嗎? 事實上是不可能的
第一, 通常程式的行數都是很龐大的, 第二, 即使程式不長, 但是程式讀入的data, 它的值域是很龐大的, 若是32 bits的 integer, 範圍是2147483647~-2147483648(大約是這樣, 我沒有記太清楚), 你如何確保每個數字進去都正確, 而且你可能是不只一個data. 所以二者組合起來, 應該也是一個天文數字吧!!
所以到這裡你可以知道要測試的狀況是無限的, 你不可能有完整測試. 因此你必須要sampling.
我想可以從測試方法中, 來印證測試真的是sampling. 你知道為什麼會有statement coverage, branch coverage 或是decision coverage嗎?
當初科學家在想有這麼多組合, 那要怎麼挑選test case呢? 那找會經所有statements的test cases, 這樣會把所有 statement都測過. 可是後來有人想, 即使測過所有statements, 還是會漏掉一些branch不會經過, 所以這樣的取樣不夠好, 因此改成取樣會經過所有branch的test cases. 可是後來又想經過所有branch, 還是不足, 因為有些decision 不會包含.
因此你會發現到, 每種測試方法都是在取樣, 只是取樣方法不同, 嚴謹度不同, 因此會有不同種類或數量的test case出來.
看到這裡, 你會知道測試是測不完的, 因為組合真的太多了. 我們可以做的, 是加強取樣的能力. 不過要小心的是, 不要選太多沒有價值也就是沒有代表性的test cases, 那不會有什麼幫助的.

其實這是工廠生產的思維模式,就如 "return a+b" 實在用不上測試不同 的組合來驗證其真確性。 再者,一個 method 的代碼應該很短才是(詳情請參考 "Clean Code" 一 書),測試就應該更簡單。 如果以 TDD 來開發,這更不會是 Sampling 的問題,因為代碼只是滿足 測試,每次加的代碼/測試都是很小的,更加不會有一次過編寫了很多不 同的 branches 而漏了測試某些組合的情況。
其實這不是只有我這樣想的, 像是Gerald M. Weinberg這位大師在 他的書中也有提到 Perfect Software: And Other Illusions about Testing by Gerald M. Weinberg Ch3 Why Not Just Test Everything? 重點有兩個 1. 要測試東西太多 基本上從網路可以找到很多文章討論 2. 能夠測試的時間太少 所以才需要抽樣... 至於clean code或是TDD在我看來他們對於提昇程式品質有很大的幫 助, 但是若是說他們能cover所有組合, 基本上我是持保留態度. 畢 竟我還聽過有沒有bug的程式, 差別只是它有很多還是很少. 即使很 少也可能是用的人不夠多吧
我覺得前兩樓似乎忽略了一些事。 TDD 應由 developer 自己做,屬於白箱測試。目的在確保單一模組 功能的正確性。 tester 要做整合多個模組,做黑箱測試,以各種 (亂槍打鳥的) 方 式,企圖找出整合後可能產生的新問題。 此文是由 tester 的角度來看測試,因此良好的取樣方式是非常重要 的。 整合後為何會有新問題呢? 就單以 return a+b 為例,仍然有可能超過數字最大上限,或下限。 超過時該怎麼辦? 或者要在更前面的地方限制 a 與 b 的上、下限 值,以完全避免超過數字最大上、下限的可能。 而 a、b 怎麼來? 它可能經過一大堆類別、函式。 UI 已經限制只收 0~100 的 double float,禁止按 "-" 鍵,但卻 忘了可以用貼上的方式把 "-" 字元貼進文字方塊。接下來的程式都沒 對值域再做檢查,於是負值就不下心流到後面去。 然後中間經過一個網路抓來的函式庫,用的型別卻是 float,於是一 些小數位數就流失掉了。complier 有可能發出警告,但很可能被人 用強制轉型來把警告消音。 然後又經過一段多執行緒程式,這段程式沒問題。但裡面有呼叫到另 一個不為 thread-safe 的類別,裡面用的遞增函式有 race condition 問題,偶爾會跑出 101 的結果。 然後再經過一個很久以前寫的類別,那個類別要求的值域是 負最大值 ~99,超過範圍會丟出例外。以前這個類別用得好好的,抄來用時沒注 意到值域不同。 各別類別自己看起來都沒問題,TDD 都完全通過,但串起來就是可以 出現新的問題,是 TDD 無法發現的。 這就是 tester 要以取樣方法來抓出的新問題。
我不確定TDD這觀念是只侷限於unit testing level. 或者他也可 以套用到integration level. 但是若是比較unit testing和integration testing的差別, 我之 前也有找到文章介紹過 http://www.wretch.cc/blog/kojenchieh/13364886
TDD 的確針對 unit level 的事情,不過在檢收層次,就有 Acceptance-TDD 或者 Behaviour Driven Development 又或者 Executable Specification 的去配合。 ATDD 的重要性在於開發人員,測試人員和產品負責人/用戶代表一起合 作去填寫相關內容。(可以參考 Gojko Adzic 寫的 Bridging the Communication Gap: Specification by Example and Agile Acceptance Testing 寫的很好,值得一看) TDD 的確不能保障所有問題不會出現,不過,Bug 也有很多種層次,得要 從不同地方著手,如果針對不同組合列表的情況 TDD + ATDD 還是挺有效 的(當然他們都不是Silver Bullet啦 :)),
"Gojko Adzic 寫的 Bridging the Communication Gap: Specification by Example and Agile Acceptance Testing 寫的很好,值得一看)" 這本書我有買耶, 不過一直還供在書架上, 看起來大家都有推薦, 改天應該花點時間看一下