什麼是單元測試?如何做好單元測試?

什麼是單元測試?如何做好單元測試?

單元測試是指,對軟件中的最小可測試單元在與程序其他部分相隔離的情況下進行檢查和驗證的工作,這裏的最小可測試單元通常是指函數或者類。
 
單元測試都是以自動化的方式執行,所以在大量回歸測試的場景下更能帶來高收益。
 
單元測試代碼裏提供函數的使用示例,因爲單元測試的具體表現形式就是對函數以各種不同輸入參數組合進行調用。
 
如何做好單元測試?
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)代碼覆蓋率越往後越難提高。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章