軟體測試基礎知識必備之淺談單元測試

什麼是單元測試?

單元測試是指,對軟體中的最小可測試單元在與程式其他部分相隔離的情況下進行檢查和驗證的工作,這裡的最小可測試單元通常是指函式或者類。

單元測試都是以自動化的方式執行,所以在大量回歸測試的場景下更能帶來高收益。

單元測試程式碼裡提供函式的使用示例,因為單元測試的具體表現形式就是對函式以各種不同輸入引數組合進行呼叫。

如何做好單元測試?

1)程式碼的基本特徵與產生錯誤的原因

無論是開發語言還是指令碼語言,都會有條件分支、迴圈處理和函式呼叫等最基本的邏輯控制,如果拋開程式碼需要實現的具體業務邏輯,僅看程式碼結構的話,所有的程式碼都是在對資料進行分類處理,每一次條件判定都是一次分類處理,巢狀的條件判定或者迴圈執行,也是在做分類處理。

如果有任何一個分類遺漏,都會產生缺陷;如果有任何一個分類錯誤,也會產生缺陷;如果分類正確也沒有遺漏,但是分類時的處理邏輯錯誤,也會產生缺陷。

2)單元測試用例詳解

單元測試的用例是一個“輸入資料”和“預計輸出”的集合。需要針對確定的輸入,根據邏輯功能推算出預期正確的輸出,並且以執行被測試程式碼的方式進行驗證。即“在明確了程式碼需要實現的邏輯功能的基礎上,什麼輸入,應該產生什麼輸出”。

單元測試用例“輸入資料”種類

被測試函式的輸入引數;

被測試函式內部需要讀取的全域性靜態變數;

被測試函式內部需要讀取的成員變數;

函式內部呼叫子函式獲得的資料;

函式內部呼叫子函式改寫的資料;

嵌入式系統中,在中斷呼叫時改寫的資料;

……

“預計輸出”:

被測試函式的返回值;

被測試函式的輸出引數;

被測試函式所改寫的成員變數;

被測試函式所改寫的全域性變數;

被測試函式中進行的檔案更新;

被測試函式中進行的資料庫更新;

被測試函式中進行的訊息佇列更新;

……

3)驅動程式碼,樁程式碼和Mock程式碼

驅動程式碼是用來呼叫被測函式的,而樁程式碼和Mock程式碼是用來代替被測函式呼叫的真實程式碼的。

軟體測試基礎知識必備之淺談單元測試

驅動、樁和Mock程式碼的關係

驅動程式碼(Driver)指呼叫被測函式的程式碼,在單元測試過程中,驅動模組通常包括呼叫被測函式錢的資料準備、呼叫被測函式以及驗證相關結果三個步驟。

程式碼樁(Stub)是用來代替真是程式碼的臨時程式碼。比如,某個函式A的內部實現中呼叫了一個尚未實現的函式B,為了對函式A的邏輯進行測試,那麼就需要模擬一個函式B,這個模擬的函式B的實現就是所謂的樁程式碼。

虛擬碼:

軟體測試基礎知識必備之淺談單元測試

為了實現函式A的全路徑覆蓋,你需要控制不同的測試用例中函式B的返回值,那麼樁函式B的虛擬碼就應該是:

軟體測試基礎知識必備之淺談單元測試

當執行第一個測試用例的時候,樁函式B應該返回true,而執行第二個測試用例的時候,樁函式B應該返回false。這樣就覆蓋了被測試函式A的if-else的兩個分支。

樁程式碼的應用首先起到了隔離和補齊的作用,使被測程式碼能夠獨立編譯、連結,並獨立執行。同時,樁程式碼還具有控制被測函式執行路徑的作用。

編寫樁程式碼通常需要遵守以下三個原則:

樁函式要具有與原函式完全相同的原形,僅僅是內部實現不同,這樣測試程式碼才能正確連結到樁函式;

用於實現隔離和補齊的樁函式比較簡單,只需保持原函式的宣告,加一個空的實現,目的是透過編譯連結;

實現控制功能的樁函式是應用最廣泛的,要根據測試用例的需要,輸出合適的資料作為被測函式的內部輸入。

Mock程式碼和樁程式碼的本質區別是:測試期待結果的驗證(Assert and Expectiation)。

對於Mock程式碼來說,我們的關注點是Mock方法有沒有被呼叫,以書面樣的引數被呼叫,被呼叫的次數,以及多個Mock函式的先後呼叫順序。所以,在使用Mock程式碼的測試中,對於結果的驗證(也就是assert),通常出現在Mock函式中。

對於樁程式碼來說,我們的關注點是利用Stub來控制被測函式的執行路徑,不會去關注Stub是否被呼叫以及怎麼樣被呼叫。所以,你在使用Stub的測試中,對於結果的驗證,通常出現在驅動程式碼中。

實際專案中如何開展單元測試?

1)並不是所有的程式碼都要進行單元測試。通常只有底層模組或者核心模組的測試中才會採用單元測試。

2)你需要確定單元測試框架的選型,這和開發語言直接相關。比如,Java最常用的單元測試框架是Junit和TestNG;C/C++最常用的單元測試框架是CppTest和Parasoft C/C++test;框架選型完成後,你還需要對樁程式碼框架和Mock程式碼框架選型,選型的主要依據是開發所採用的具體技術棧;通常,單元測試框架、樁程式碼/Mock程式碼的選型工作由開發架構師和測試架構師共同決定。

3)為了能夠衡量單元測試的程式碼覆蓋率,通常你還需要引入計算程式碼覆蓋率的工具。不同的語言會有不同的程式碼覆蓋率統計工具,比如Java的JaCoCo,JavaScript的Istanbul。

4)最後你需要把單元測試執行、程式碼覆蓋率統計和持續整合流水線做整合。以確保每次程式碼遞交,都會自動觸發單元測試,並在單元測試執行過程中自動統計程式碼覆蓋率,最後以“單元測試透過率”和“程式碼覆蓋率”為標準來決定背刺程式碼遞交是否能夠被接受。

常見問題

在專案中全民推行單元測試時,你會發現還有一些困難需要克服:

1)緊密耦合的程式碼難以隔離;

2)隔離後編譯連結執行困難;

3)程式碼本身的可測性較差,通常程式碼的可測試性和程式碼規模成正比;

4)無法透過樁程式碼直接模擬系統底層函式的呼叫;

5)程式碼覆蓋率越往後越難提高。