白箱測試是根據程式內部結構來測試系統. 又稱為結構化測試 (Structural testing). 老實說, 這個方法在業界不太會被使用. 並且也常常被誤解, 因此我只打算用簡單篇幅介紹. 重點在讓大家有基本常識, 不要被人騙了還不知道.
白箱測試方法可以應用到的場景很廣, 可以應用到以下測試階段
單元測試
整合測試
系統測試
驗收測試
不同階段使用白箱測試, 差別在於用起來有多困難. 有些階段或是場景用這種方法開立測試很容易, 有些階段用這些方法不容易. 例如: 如果在單元測試階段使用白箱測試, 程式碼很短, 所以很容易就開出測試個案. 但是如果到了系統測試或是驗收測試階段, 你面對的是一整個系統, 要如何去查看程式碼來開立測試個案呢?
白箱測試的優缺點如下:
• 優點
因為是從程式碼的角度出發, 如果有抓到錯誤, 比較可以準確找出錯誤點
通常會使用白箱測試, 大都是在單元測試階段, 所以可以在開始或是最源頭發現錯誤
根據內部結構或是設計架構設計測試個案, 所以比較可以拳拳到肉, 精準根據問題去開立測試場景
因為有測試自動化的保護網, 可以開發人員可以利用受測程式去調整程式碼
• 缺點
因為要根據程式碼來開立測試個案, 所以需要程式設計的知識
當程式有變動後, 需要再修改測試 script
前面提到白箱測試程式內部結構來測試系統. 那何謂程式內部結構? 這裡是大家最不明白的地方, 讓我來解釋一下.
程式裡面有什麼呢? 如圖 10-1 所示, 程式內部有兩類東西:
程式裡面就是有控制流程, 像是 if, while, for 等東西, 所以我們開立測試個案來確認這些控制流程是否有問題. 所以你才聽到有 line coverage (statement coverage, branch coverage, condition coverage 這些東西.
程式裡面還有另一個東西就是變數. 變數在程式裡面會有宣告, 有被設定數值, 有被使用或是參考到數值. 我們要測試變數在這些狀況是否正確. 但是這部分在業界中幾乎沒有人這樣測試. 所以讓大家知道有這個東西, 到這裡就好.
圖 10-1 白箱測試中所謂程式內部結構是什麼
所以當你在測試一個函式, 例如 fun (a, b). 你如果測試作法如下, 就是輸入數值來呼叫受測函式. 例如
fun (2, 3)
fun (0, -1)
這樣只是黑箱測試, 因為你沒有特別去看函式內部程式怎麼寫, 沒有想說要測試經過哪幾行, 或是哪個 branch, 或是哪個控制指令. 純粹只是看這個函式需要什麼輸入, 根據要測試的場景決定輸入值就來測試.
所以大多數人以為他做單元測試就是在進行白箱測試, 這個想法是錯的, 因為只是把這個受測單元當作黑箱來測.
如果單元測試就是白箱測試, 維基百科直接寫單元測試就是白箱測試, 或是白箱測試就是單元測試就好. 你可以分別去查維基百科的 unit testing 或 white box testing, 不會有這樣的寫法. 所以不用往自己臉上貼金, 說你在執行白箱測試.
白箱測試 - wiki
https://zh.wikipedia.org/zh-tw/%E7%99%BD%E7%9B%92%E6%B5%8B%E8%AF%95
單元測試
https://zh.wikipedia.org/zh-tw/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95
此外, 如果要真的根據程式控制邏輯或是變數來開立測試個案, 大多應該是要有工具來幫忙, 沒有工具的話, 大家就變成是在單元測試階段進行, 因為程式碼比較短, 比較有機會去分析程式碼. 所以我前面才會說, 業界不太可能用這樣的作法, 一方面要有時間去做分析, 另一方面沒有工具做不了, 不是所有語言或平台都有工具支援.
接下來我們簡單看幾個白箱測試的作法:
Line Coverage
Line Coverage 就是度量你的測試, 經過多少行受測程式代碼的比例. 公式如下所示
Line Coverage = 被執行過的行數 / 程式總共行數 x 100%
所以 Line Coverage 又稱為 Statement Coverage.
讓我們來一個範例, 下圖 10-2 所示. 這個程式總共 4 行, 所以只需要一個測試個案, 經過 1 → 2 → 3 → 4, 這樣就可以達到 100% Line Coverage. 所以 Line Coverage 基本上很容易達成.
圖 10-2 Line Coverage 的範例
Branch Coverage
Branch Coverage 就是度量你的測試, 經過多少個受測程式分支的比例. 公式如下所示
Branch Coverage = 被執行過的分支數 / 程式總共分支數 x 100%
圖 10-3 Branch Coverage 的範例
讓我們來一個範例, 如上圖 10-3 所示. 這個程式總共 4 行, 其中 Line 是一個 if 的敘述, 所以他有兩個分支.
分支 1: 1 → 2 → 3 → 4 (經過 True 的分支)
分支 2: 1 → 2 → 4 (經過 False 的分支)
需要兩個測試個案, 分別經過各個分支, 這樣就可以達到 100% Branch Coverage.
從這個範例中, 你可以發現 100% Branch Coverage 會涵蓋 100% Line Coverage. 因此 100% 不是重點, 重點是後面是選擇哪一個方法, 是 Line, Branch, Condition 或是其他的.
像有些是 Function Coverage, 就是看有多少函式被涵蓋. 這就是基本到不能再基本, 所以 100% Function Coverage 能跟 Branch Coverage 比嗎?
所以談到這裡, 讓我們對涵蓋度多談一點, 其實涵蓋度是非常危險的東西.
為什麼說涵蓋度是非常危險的東西呢?因為從他的定義中, 它只告訴你什麼東西有經過. 它就只有提供這種資訊, 就沒有其他資訊了.
測試涵蓋度其實沒告訴你以下資訊, 但是這些資訊卻對測試很重要:
受測程式都沒有處理例外狀況
只要不處理錯誤狀況, 都假設一切正確, 讓程式大多是循序, 沒有太多分支, 這樣只要少量的測試個案, 就可以經過所以程式代碼.
如何檢查通過
很多測試程式只是檢查傳回值, 有些開發人員的傳回值都是 true …. 所以遇到這種狀況涵蓋度就是屎
使用什麼測試資料
你覺得我用 2 跟 3 去測, 有人用 0 跟 -1 去錯, 哪個人找到錯誤的機率高些? 可能是 0 跟 -1高些, 所以他只告訴你有經過, 但是沒說用什麼資料測. 你覺得都是用 happy data, 這樣的測試有意義嗎?
重要或不重要的功能
有些程式片段很重要, 有些功能根本就沒人用. 前者沒經過會死人, 後者沒測過應該也還好. 但是只有涵蓋度的數字, 是看不出來這種事.
但是測試涵蓋度也是不都沒用. 較低的涵蓋度只是告訴你測得不夠, 但較高的涵蓋度不代表測得完整. 你應該用下面方式來善用測試涵蓋度
• 用涵蓋度來討論這些地方為什麼沒經過
• 至少讓涵蓋度 大於 0
• 可以規範至於大於多少涵蓋度才允許部署到 production
• 有修改或新增的地方, 都要補測試個案去涵蓋
作者對白箱測試的說明真的突破盲腸,叫自以為是在進行白箱測試的人汗顏。不過叫人尷尬一定不是作者本意,能使白箱測試真正發揮作用才是重點