單元測試基礎

1.1單元測試的定義

單元測試就是針對一個工做單元設計的測試,這裏的「工做單元」是指對一個工做方法的要求。
單元測試是開發者編寫的一小段代碼,用於檢測被測代碼的一個很小的、很明確的功能是否正確。一般而言,一個單元測試用於判斷某個特定條件(或場景)下某個特定函數的行爲。算法

例:
你可能把一個很大的值放入一個有序list中去,而後確認該值出如今list的尾部。或者,你可能會從字符串中刪除匹配某種模式的字符,而後確認字符串確實再也不包含這些字符了。
執行單元測試,就是爲了證實某段代碼的行爲和開發者所指望的一致!數據庫

//被測方法
  public double Add(double a, double b)
  {
      return a + b;
  }      
  
  //測試方法
  [Test]
  public void AddTest()
  {
      double result = new Calculator().Add(14, 15);
      Assert.AreEqual(30,result);
  }

1.2工做單元

調用系統的一個公共方法到產生一個測試可見的最終結果,其間這個系統發生的行爲總稱爲一個工做單元。咱們經過系統的公共AP和行爲就能夠觀察到一個可見的最終結果,無需查看系統的內部狀態。一個最終結果能夠是如下任何一種形式。網絡

  1. 被調用的公共方法回一個值(一個返回值不爲空的函數)
  2. 在方法調用的先後,系統的狀態或行爲有可見的變化,這種變化無需查詢私有狀態便可判斷。(例如:一個之前不存在的用戶能夠登入系統,或者一個狀態機系統的屬性發生變化。)
  3. 調用了一個不受測試控制的第三方系統,這個第三方系統不返回任何值,或者返回值都被忽略。(例如:調用一個第三方日誌系統,這個系統不是你編寫的,並且你也沒有源代碼。)

不少人以爲被測試的工做單元應該儘量的小。我卻不這麼看,我認爲工做單元這個概念意味着一個單元既能夠小到只包含一個方法,也能夠大到包括實現某個功能的多個類和函數。若是你的工做單元很大,卻可是其最終結果對用戶可見度高,易於維護也何嘗不是好的測試,相反若是試圖把工做單元縮到最小,最後會不得不僞造一堆東西反而會增長測試的複雜度,拔苗助長。函數

2.什麼不是單元測試

單元測試實際上是一門很基礎也很簡單的技術,然而在單元測試實踐過程當中,每每會對單元測試產生一些誤區,進而寫出一些不是單元測試的"單元測試" ,其中常見的主要有如下三種。性能

2.1 跨邊界的測試

單元測試背後的思想是,僅測試這個方法中的內容,測試失敗時不但願必須穿過基層代碼、數據庫表或者第三方產品的文檔去尋找可能的答案!
當測試開始滲透到其餘類、服務或系統時,此時測試便跨越了邊界,失敗時會很難找到缺陷的代碼。
測試跨邊界時還會產生另外一個問題,當邊界是一個共享資源時,如數據庫。與團隊的其餘開發人員共享資源時,可能會污染他們的測試結果!單元測試

2.2 不具備針對性的測試

若是發現所編寫的測試對一件以上的事情進行了測試,就可能違反了「單一職責原則」。從單元測試的角度來看,這意味着這些測試是難以理解的非針對性測試。隨着時間的推移,向類或方法種添加了更多的不恰當的功能後,這些測試可能會變的很是脆弱。診斷問題也將變得極具備挑戰性。
如:StringUtility中計算一個特定字符在字符串中出現的次數,它沒有說明這個字符在字符串中處於什麼位置也沒有說明除了這個字符出現多少次以外的其餘任何信息,那麼這些功能就應該由StringUtility類的其它方法提供!一樣,StringUtility類也不該該處理數字、日期或複雜數據類型的功能!測試

2.3 不可預測的測試

單元測試應當是可預測的。在針對一組給定的輸入參數調用一個類的方法時,其結果應當老是一致的。有時,這一原則可能看起來很難遵照。例如:正在編寫一個日用品交易程序,黃金的價格可能上午九時是一個值,14時就會變成另外一個值。
而好的設計原則就是將不可預測的數據的功能抽象到一個能夠在單元測試中模擬(Mock)的類或方法中編碼

2.4 集成測試

其實上面三種測試已經到了集成測試的領域。任何測試,若是它運行速度不快,結果不穩定,或者要用到被測試單元的一個或多個真實依賴物,咱們就認爲它是集成測試。
集成測試是對一個工做單元進行的測試,這個測試對被測試的工做單元沒有徹底的控制,並使用該單元的一個或多個真實依賴物,例如時間、網絡、數據庫、線程或隨機數產生器等。
集成測試自己並非一種壞事,反而其具備和單元測試同樣高的地位,可是在實踐過程當中咱們把集成測試和單元測試分離開來仍是很重要的。線程

3.優秀的單元測試有哪些特性

單元測試是很是有魔力的魔法,也是一把雙刃劍。使用得當,能夠頗有效的提升咱們的編碼質量,提高研發效率,可是若是使用不恰當亦會浪費大量的時間在測試編碼、維護和調試上從而影響代碼和整個項目,徒勞而無功!
所以作好單元測試相當重要!而想要作好單元測試,咱們首先應該知道優秀的單元測試有哪些特性。
一個好的單元測試必定是有如下幾個特性的
• 自動化
• 完全的
• 可重複的
• 獨立的
• 專業的
回顧一下本身之前寫過的單元測試問本身幾個問題。設計

  1. 它是否是能夠自動化一鍵運行、而且能夠重複運行

  2. 幾個月後它是否是仍能夠運行、而且獲得指望的結果

  3. 它是否能夠在幾分鐘內運行結束

  4. 在運行以前你是否不須要須要進行一系列的配置

  5. 每次運行是否可以獲得相同的結果

  6. 外部的系統因素是否不會影響你的測試結果

  7. 測試代碼是否很簡單就能夠編寫完成

若是針對以上問題有任何一個的回答是「否」,那麼你應該好好的思考一下到底如何去作好單元測試。

4. 如何進行單元測試

對於一個方法或者類,乍一看就能找出其隱藏深處的bug是很不容易的,所以在bug挖掘方面一般會有一些經驗和套路,來指導咱們更好的進行單元測試。

3.1 測試哪些內容

通常來講有六個值得測試的具體方面,能夠把這六個方面統稱爲Right-BICEP:

  • Right——結果
    對於單元測試測試而言,首要的也是最明顯的任務就是查看所指望的結果是否正確,例如判斷一個方法的返回值是否爲序列中的最大值......
  • B——邊界條件
    找邊界條件是作單元測試中最有價值的工做之一,由於bug通常就出如今邊界上。關於邊界條件2會有詳細總結
  • I——檢查反向關聯
    對於一些方法,咱們可使用反向的邏輯關係來驗證它們。例如,你能夠用對結果進行平方的方式來檢查一個計算平方根的函數,而後測試結果是否和原數據很接近
  • C——交叉檢查
    有些時候咱們實現一個問題會有不一樣的算法,在生產系統中咱們使用一種算法,而在測試中咱們可使用另外一種算法來驗證其結果是否一致。
  • E——強制產生錯誤條件
    在實際運行過程當中,有時候會發生一些意外的難以免的錯誤,例如磁盤會滿,網絡連線會斷開.....從而致使程序崩潰。咱們應該在測試中強制引起錯誤,來測試代碼是否可以按照預期處理這些異常。
  • P——是否知足性能條件
    性能一樣是咱們測試過程當中須要驗證的指標

3.2 注意邊界條件

代碼中的許多Bug常常出如今邊界條件附近,對於邊界條件的測試咱們能夠從CORRECT七個方面進行考慮

  • 一致性----值是否知足預期的格式
  • 有序性----一組值是否知足預期的排序要求
  • 區間性----值是否在一個合理的最大值最小值範圍內
  • 引用、耦合性----代碼是否引用了一些不受代碼自己直接控制的外部因素
  • 存在性----值是否存在(例如:非Null,非零,存在於某個集合中)
  • 基數性----是否剛好具備足夠的值
  • 時間性----全部事情是否都按照順序發生的?是否在正確的時間、是否及時

3.3 使用Mock對象

單元測試的目標是驗證咱們的工做單元,可是若是這個工做單元依賴一些其餘的對象或是一些難以操控的東西,好比網絡、數據庫等。這時咱們就要使用mock對象,使得在運行UT的時候使用的那些難以操控的東西其實是咱們mock的對象,而咱們mock的對象則能夠按照咱們的意願返回一些值用於測試。通俗來說,Mock對象就是真實對象在咱們調試期間的測試品。對於外部對象內的邏輯咱們並不關心,咱們只須要讓它給咱們返回咱們想要的值,來驗證咱們的業務邏輯便可

IFileExtensionManager fileManager;

public bool IsValidFileName(){
    //獲取文件擴展名
    string extName=fileManager.GetExtName();
    if(extName=="jpg"){
        return true;
    }
    return false;
}

如上示例,假設從文件系統中讀取一個文件,獲取文件的擴展名,若是擴展名是jpg就返回true,不然返回false。
注意,這裏咱們要測試的邏輯是若是擴展名是jpg就返回true,不然返回false。而對於fileManager.GetExtName()方法內部的邏輯是什麼樣的的咱們是不關心的,咱們只須要mock這個方法使其返回咱們想要的值就能夠了。
關於具體如何去mock工做單元中的一些外部依賴,會在存根與模擬對象裏面詳細進行總結。

總結

本文總結了什麼是單元測試、什麼不是單元測試以及優秀的單元測試有哪些特性,簡單介紹瞭如何進行單元測試。 編寫差勁的單元測試是沒有意義的,我看到過不少公司嘗試去實踐單元測試,但最終要麼在某個階段放棄了,要麼並無真正執行單元測試。最終仍是依賴集成測試或者人工測試來發現問題,不得不以失敗而了結,並冠冕堂皇的認爲單元測試是一個耗時好力而無功的雞肋東西。 所以若是你想要真正的去實踐單元測試,那麼必須充分的理解到底什麼是單元測試,已經如何去更好的進行實踐優秀的單元測試。 而對於如何更好的去實踐單元測試,後續會結合實踐用更多的篇幅去總結分享。

相關文章
相關標籤/搜索