一、單元測試
1. 單元測試是什么
單元是應用的最小可測試部件。在過程化編程中,一個單元就是單個程序、函數、過程等;對於面向對象編程,最小單元就是方法,包括基類、超類、抽象類等中的方法。單元測試就是軟件開發中對最小單位進行正確性檢驗的測試工作。
不同地方對單元測試有的定義可能會有所不同,但有一些基本共識:
- 單元測試是比較底層的,關注代碼的局部而不是整體。
- 單元測試是開發人員在寫代碼時候寫的。
- 單元測試需要比其他測試運行得快。
2. 單元測試的意義
- 提高代碼質量。代碼測試都是為了幫助開發人員發現問題從而解決問題,提高代碼質量。
- 盡早發現問題。問題越早發現,解決的難度和成本就越低。
- 保證重構正確性。隨着功能的增加,重構(修改老代碼)幾乎是無法避免的。很多時候我們不敢重構的原因,就是擔心其它模塊因為依賴它而不工作。有了單元測試,只要在改完代碼后運行一下單測就知道改動對整個系統的影響了,從而可以讓我們放心的重構代碼。
- 簡化調試過程。單元測試讓我們可以輕松地知道是哪一部分代碼出了問題。
- 簡化集成過程。由於各個單元已經被測試,在集成過程中進行的后續測試會更加容易。
- 優化代碼設計。編寫測試用例會迫使開發人員仔細思考代碼的設計和必須完成的工作,有利於開發人員加深對代碼功能的理解,從而形成更合理的設計和結構。
- 單元測試是最好的文檔。單元測試覆蓋了接口的所有使用方法,是最好的示例代碼。而真正的文檔包括注釋很有可能和代碼不同步,並且看不懂。
3. 單元測試用例編寫的原則
3.1 理論原則
- 快。單元測試是回歸測試,可以在開發過程的任何時候運行,因此運行速度必須快
- 一致性。代碼沒有改變的情況下,每次運行得結果應該保持確定且一致
- 原子性。結果只有兩種情況:Pass / Fail
- 用例獨立。執行順序不影響;用例間沒有狀態共享或者依賴關系;用例沒有副作用(執行前后環境狀態一致)
- 單一職責。一個用例只負責一個場景
- 隔離。功能可能依賴於數據庫、web 訪問、環境變量、系統時間等;一個單元可能依賴於另一部分代碼,用例應該解除這些依賴
- 可讀性。用例的名稱、變量名等應該具有可讀性,直接表現出該測試的目標
- 自動化。單元測試需要全自動執行。測試程序不應該有用戶輸入;測試結果應該能直接被電腦獲取,不應該由人來判斷。
3.2 規約原則
在實際編寫代碼過程中,不同的團隊會有不同團隊的風格,只要團隊內部保持有一定的規約即可,比如:
- 單元測試文件名必須以 xxx_test.go 命名
- 方法必須是 TestXxx 開頭,建議風格保持一致(駝峰或者下划線)
- 方法參數必須 t *testing.T
- 測試文件和被測試文件必須在一個包中
3.3 衡量原則
單元測試是要寫額外的代碼的,這對開發同學的也是一個不小的工作負擔,在一些項目中,我們合理的評估單元測試的編寫,我認為我們不能走極端,當然理論上來說全寫肯定時好的,但是從成本,效率上來說我們必須做出權衡,衡量原則如下:
- 優先編寫核心組件和邏輯模塊的測試用例
- 邏輯類似的組件如果存在多個,優先編寫其中一種邏輯組件的測試用例
- 發現 Bug 時一定先編寫測試用例進行 Debug
- 關鍵 util 工具類要編寫測試用例,這些 util 工具適用的很頻繁,所以這個原則也叫做熱點原則,和第 1 點相呼應。
- 測試用戶應該獨立,一個文件對應一個,而且不同的測試用例之間不要互相依賴。
- 測試用例的保持更新
4. 單元測試用例設計方法
4.1 規范(規格)導出法
規范(規格)導出法將需求”翻譯“成測試用例。
例如,一個函數的設計需求如下:
函數:一個計算平方根的函數 輸入: 實數 輸出: 實數 要求: 當輸入一個 0 或者比 0 大的實數時,返回其正的平方根;當輸入一個小於 0 的實數時,顯示錯誤信息“平方根非法—輸入之小於 0”,並返回 0;庫函數
printf()
可以用來輸出錯誤信息。
在這個規范中有 3 個陳述,可以用兩個測試用例來對應:
- 測試用例 1:輸入 4,輸出 2。
- 測試用例 2:輸入-1,輸出 0。
4.2 等價類划分法
等價類划分法假定某一特定的等價類中的所有值對於測試目的來說是等價的,所以在每個等價類中找一個之作為測試用例。
- 按照 [輸入條件][有效等價類][無效等價類] 建立等價類表,列出所有划分出的等價類
- 為每一個等價類規定一個唯一的編號
- 設計一個新的測試用例,使其盡可能多地覆蓋尚未被覆蓋地有效等價類。重復這一步,直到所有的有效等價類都被覆蓋為止
- 設計一個新的測試用例,使其僅覆蓋一個尚未被覆蓋的無效等價類。重復這一步,直到所有的無效等價類都被覆蓋為止
例如,注冊郵箱時要求用 6~18 個字符,可使用字母、數字、下划線,需以字母開頭。
測試用例:
4.3 邊界值分析法
邊界值分析法使用與等價類測試方法相同的等價類划分,只是邊界值分析假定 錯誤更多地存在於兩個划分的邊界上。
邊界值測試在軟件變得復雜的時候也會變得不實用。邊界值測試對於非向量類型的值(如枚舉類型的值)也沒有意義。
例如,和4.1相同的需求: 划分(ii)的邊界為 0 和最大正實數;划分(i)的邊界為最小負實數和 0。由此得到以下測試用例:
- 輸入 {最小負實數}
- 輸入 {絕對值很小的負數}
- 輸入 0
- 輸入 {絕對值很小的正數}
- 輸入 {最大正實數}
4.4 基本路徑測試法
基本路徑測試法是在程序控制流圖的基礎上,通過分析控制構造的環路復雜性,導出基本可執行路徑集合,從而設計測試用例的方法。設計出的測試用例要保證在測試中程序的每個可執行語句至少執行一次。
基本路徑測試法的基本步驟:
- 程序的控制流圖:描述程序控制流的一種圖示方法。
- 程序圈復雜度:McCabe 復雜性度量。從程序的環路復雜性可導出程序基本路徑集合中的獨立路徑條數,這是確定程序中每個可執行語句至少執行一次所必須的測試用例數目的上界。
- 導出測試用例:根據圈復雜度和程序結構設計用例數據輸入和預期結果。
- 准備測試用例:確保基本路徑集中的每一條路徑的執行。