C# 單元測試(入門)

注:本文示例環境數組

VS2017
XUnit 2.2.0 單元測試框架
xunit.runner.visualstudio 2.2.0 測試運行工具
Moq 4.7.10 模擬框架網絡

什麼是單元測試?

確保軟件應用程序按做者的指望執行操做,其中最好的一種方法是擁有自動化測試套件。 能夠對軟件應用程序進行各類不一樣的測試,包括集成測試、Web 測試、負載測試等。 測試各個軟件組件或方法的單元測試是最低級測試。框架

所謂單元測試(unit testing),就是開發者編寫的一小段代碼,用於對軟件中的最小單元進行檢查和驗證,其通常驗證對象是一個函數或者一個類。一般而言,一個單元測試是用於判斷某個特定條件(或者場景)下某個特定函數的行爲。編輯器

爲何要使用單元測試?

  • 大大節約了測試和修改的時間,有效且便於測試各類狀況。
  • 能快速定位bug(每個測試用例都是具備針對性)。
  • 能使開發人員從新審視需求和功能的設計(難以單元測試的代碼,就須要從新設計)。
  • 強迫開發者以調用者而不是實現者的角度來設計代碼,利於代碼之間的解耦。
  • 自動化的單元測試能保證迴歸測試的有效執行。
  • 使代碼能夠放心修改和重構。
  • 測試用例,可做爲開發文檔使用(測試即文檔)。
  • 測試用例永久保存,支持隨時測試。

對於我我的來講,主要是防止本身犯低級錯誤的,同時也方便修改(BUG修復)而不引入新的問題。能夠放心大膽的重構。簡言之,這個簡單有效的技術就是爲了令代碼變得更加完美。ide

既然單元測試有這些好處,爲何咱們不去用呢?

能夠概括爲如下幾個理由。函數

  1. 對單元測試存在的誤解,如:單元測試屬於測試工做,應該由測試人員來完成,因此單元測試不屬於開發人員的職責範圍。工具

    答:雖然單元測試雖然叫作"測試",但實際屬於開發範疇,應該由開發人員來作,而開發人員也能從中受益。單元測試

  2. 沒有真正意識到單元測試的收益,認爲寫單元測試太費時,不值得。測試

    答:在開發時越早發現bug,就能節省更多的時間,下降更多的風險。單元測試先期要編寫測試用例,是須要多耗費些時間,可是後面的調試、自測,均可以經過單元測試處理,不用手工一遍又一遍處理。實際上總時間被減小了。spa

  3. 項目經理或技術主管沒有要求寫單元測試,因此不用寫。

    答:寫單元測試應該成爲開發人員的一種本能,開發自己就應該包含單元測試。

  4. 不知道有單元測試這回事,不知道如何用。通過這篇文檔的說明,就基本知道如何處理單元測試。

框架選型

經常使用單元測試框架:MSTest (Visual Studio官方)、XUnit 和 NUnit。

  1. MS Test爲微軟產品,集成在Visual Studio 2008+工具中。
  2. NUnit爲.Net開源測試框架(採用C#開發),普遍用於.Net平臺的單元測試和迴歸測試中,官方網址(www.nunit.org)。
  3. XUnit.Net爲NUnit的改進版。

(如下主要講解MSTest 和NUnit的使用,XUnit操做和NUnit操做基本相似)

基礎實踐

開始建立你的第一個的單元測試項目吧

1)  咱們先來用 VS2017 中自帶的測試模塊(MSTest)來寫一個簡單的單元測試吧。
  1. 新建一個Solution,並添加項目UnitTestDemo(用於編寫被測試的項目)

  2. 在該工程中添加UnitTestClass類,並書寫一個靜態的GetTriangle(string[] sideArr) 函數用來返回一個三角形的類型。

namespace UnitTest
{
    public class UnitTestClass
    {
        /// <summary>
        /// 獲取三角形類型.
        /// </summary>
        /// <param name="sideArr">三角形三邊長度數組.</param>
        /// <returns>返回三角形類型名稱.</returns>
        public static string GetTriangle(string[] sideArr)
        {
            string result = string.Empty;
            int a = int.Parse(sideArr[0]);
            int b = int.Parse(sideArr[1]);
            int c = int.Parse(sideArr[2]);
            if (a + b > c && a + c > b && b + c > a)
            {
                if (a == b && a == c)
                {
                    result = "等邊三角形";
                }

                if (a == b || a == c || b == c)
                {
                    result = "等腰三角形";
                }
                else
                {
                    result = "通常三角形";
                }
            }
            else
            {
                result = "不構成三角形";
            }
            return result;
        }
    }
}
  1. 而後在solution中添加一個UnitTestDemoTests測試項目,如圖所示,添加 => 新建項目以後選擇 測試 => 單元測試項目。

新建好測試項目以後,你會獲得一個UnitTest1測試類模板,即一個帶有[TestClass] attribute標記的類和一個帶有[TestMethod] attribute標記的空方法public void TestMethod1()

  1. 單元測試項目沒法自動訪問它正在測試的類庫。 能夠經過添加對類庫項目的引用來提供測試庫訪問權限。 爲此,請右鍵單擊UnitTestProject1項目,而後依次選擇「添加」 > 「引用」。在「引用管理器」對話框中,而後選擇 UnitTestDemo項目,以下圖中所示。

    

  在UnitTestDemoTests項目中添加UnitTestDemo項目的引用,如今咱們的solution就具備了下圖所示的目錄結構。

    

  5. 在UnitTestDemoTests項目中的UnitTest1類中,將模板提供的樣本單元測試代碼替換爲如下代碼:

using UnitTest;
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTestDemoTests
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod()]
        public void GetTriangle_Test()
        {
            string[] sideArr = {"5", "5", "5"};
            Assert.AreEqual("等邊三角形", UnitTestClass.GetTriangle(sideArr));
        }
    }
}

  6. 生成UnitTestDemoTests測試項目,在生成項目後,測試項將出如今測試資源管理器中。 若是測試資源管理器窗口不可見,請選擇頂級 Visual Studio 菜單上的「測試」,而後依次選擇「窗口(Windows)」、「測試資源管理器(Ctrl + E,T9)」,如圖所示。

 

  

  7. 在測試資源管理器上能夠看到剛剛所寫的測試方法,這樣在GetTriangle_Test單擊右鍵選擇「運行所選定的測試」就能夠在Test Explorer裏看到單元測試的運行結果,以下圖所示。  

  

能夠看到,咱們在單元測試中提供的例子的指望是輸出「等邊三角形」,運行結果倒是「等腰三角形」。再看一看 GetTriangle() 函數的代碼,原來是在對在判斷三邊數值是等邊三角形以後沒有使用 else if 又用 if 判斷爲等腰三角形了。經過這個簡單的單元測試就可以發現一些意向不到的錯誤。不要覺得這裏的bug很低級,相似的狀況確實會在現實中發生。

 

  8. 把上面的錯誤更正後,再次運行TestMethod1()就會獲得測試已經過的結果,如圖所示。

  

 

建立單元測試項目和測試方法,除了以上經過手動建立單元測試項目和根據你的要求進行編寫測試用例以外,還能夠從你的項目的方法上直接生成單元測試項目和單元測試存根,那樣操做更加方便,速度也會更快一些。

2) 經過代碼直接生成單元測試項目和單元測試存根
  1. 在代碼編輯器窗口中,從上下文菜單右鍵單擊並選擇「建立單元測試」。

  

  1. 在建立單元測試窗口,選擇默認值,或更改用於建立並命名單元測試項目和單元測試的參數值。 單擊「肯定」,建立單元測試項目。

  

這裏涉及測試框架的選擇,MSTest是VS自帶的測試框架。新的MS TEST如今是經過Nuget的包發佈了,目前MS發佈了兩個版本:

  • MS TEST V1:V1的版本依賴於一個包: MSTest.TestFramework
  • MS TEST V2:V2的版本依賴於兩個包: MSTest.TestFrameworkMSTest.TestAdapter
    這兩個版本使用起來仍是大同小異的,MSTest v2 主要是爲了.net core準備的,固然也能夠在.net framework上運行,而且在v1上新加入了一些擴展。
  1. 在生成的測試項目中,將測試代碼添加到對應單元測試方法中,以使單元測試有意義。
  2. 此後生成測試項目,並在測試資源管理器中運行測試方法,獲得測試結果(與上方步驟一致)。

編寫測試代碼

你使用的單元測試框架和 Visual Studio IntelliSense 將指導你完成爲代碼項目的單元測試編寫代碼。 若要在測試資源管理器中運行,大多數框架要求你添加特定的屬性來識別單元測試方法。 框架還提供了一種方法,一般經過斷言語句或方法屬性,來指示測試方法是否已經過或失敗。 其餘屬性標識可選的安裝方法,即在類初始化時和每一個測試方法和每一個拆卸方法以前的安裝方法,這些拆卸方法在每一個測試方法以後和類被銷燬以前運行。

AAA(準備、執行、斷言)模式是編寫待測試方法的單元測試的經常使用方法:

  • 準備(Arrange),單元測試方法的準備部分初始化對象並設置傳遞給待測試方法的數據;
  • 執行(Act),執行部分調用具備準備參數的待測試方法;
  • 斷言(Assert),斷言部分驗證待測試方法的執行行爲與預期相同。
如示例中驗證 UnitTest1.GetTriangle() 函數,咱們編寫了一個測試來驗證方法的標準行爲:
[TestMethod()]
public void GetTriangle_Test()
{
    // arrange  
    string[] sideArr = { "5", "5", "5" }; // 準備傳給待測試方法的數據
    string expected = "等邊三角形";

    // act  
    var actual = UnitTestClass.GetTriangle(sideArr); // 調用測試方法

    // assert  
    Assert.AreEqual(expected, actual); // 驗證待測試方法的執行結果是否與預期相同
}
爲單元測試設置超時值:

在某些狀況下(例如經過網絡獲取數據),經常不但願程序卡住而佔用太多時間,經過設置測試方法的超時時間,來測試一個方法是否在預期時間內執行。

[TestMethod()]
[Timeout(2000)] // 毫秒 要在單個測試方法上設置超時時間
public void GetTriangle_Test()
{   ... 
}
[TestMethod()] [Timeout(TestTimeout.Infinite)]
// 毫秒 將超時時間設置爲容許的最大值 public void GetTriangle_Test() { ... }
MSTest參數化測試:

什麼是參數化測試?

答:簡單的說,就是一樣的邏輯,根據輸入參數不一樣給出不一樣的結果。由於只是參數不一樣,因此並不但願把測試方法寫多遍,可是又但願對每一個參數的測試成爲一個獨立的測試用例。舉例說,假定我有一個數學計算的方法是把兩個整數相加求和,我但願證實這個方法對於任意兩個數都是經過的。

在MSTest中能夠經過DataRow Attribute 來指定測試用例的參數,實現參數化測試:

/// <summary>
/// 相加(待測試方法)
/// </summary>
/// <param name="num1">數值1</param>
/// <param name="num2">數值2</param>
/// <returns>計算結果</returns>
public static int Add(int num1, int num2)
{
    return Math.Abs(num1 + num2);
}
/// <summary>
/// 測試方法
/// </summary>
[TestMethod()]
[DataRow(10, 20)]
[DataRow(-2, -5)]
[DataRow(1, -2)]
[DataRow(5, null)]
public void Add_Test(int num1, int num2)
{
    Assert.AreEqual(UnitTestClass.Add(num1, num2), num1 + num2);
}

測試了全部可能的狀況,以達到更好的覆蓋率。上方給出示例Add方法的單元測試運行測試結果以下圖所示。

  

測試結果:測試結果指出對兩個數相加操做的方法,目標方法還取了絕對值,與相應結果不符。

 

測試調試

可使用測試資源管理器爲你的測試啓動調試會話。 使用 Visual Studio 調試程序能夠無縫地逐句得使你在單元測試和所測試項目之間來回反覆。 若要開始調試:

  1. 在 Visual Studio 編輯器中,在想要調試的一個或多個測試方法中設置斷點

  2. 在測試資源管理器中,選擇測試方法,而後點擊右鍵從快捷菜單選擇「調試選定的測試」。

    

  3. 進入調試模式

F5 繼續。

F10 執行下一行代碼,但不執行任何函數調用。

F11 在執行進入函數調用後,逐條語句執行代碼。

Shift + F11 執行當前執行點所處函數的剩餘行。

Shift + F5 中止運行程序中的當前應用程序。可用於「中斷」模式和「運行」模式。

NUnit 測試框架

 1)下載安裝NUnit插件

  咱們在VS中選擇工具菜單欄下的擴展和更新,選擇聯機並在搜索框中輸入NUnit。有2個版本的Nunit適配器,分別爲NUnit 3.x(最新版爲3.4.1)和NUnit 2.x(最新版爲2.6.4),都支持Visual Studio 2012+。若想在VS2010中集成,須要安裝NUnit 2.6.4安裝包(可在官網下載)與VS2010 NUnit整合插件下載,下載安裝完畢就能在 VS2010 的視圖=>其餘窗口中看到 Visual Nunit (或使用快捷鍵Ctrl + F7),打開該視圖,將之拖到合適的位置。

 2)建立NUnit單元測試項目

 未完待續..

 

使用 Visual Studio 2017進行實時單元測試

Live Unit Testing 是 Visual Studio 2017 版本 15.3 中提供的一項技術,可在咱們更改代碼,而後保存的時候,它會自動生成自動測試,最後得出結果。

1)實時單元測試:

  • 讓你更有信心地對代碼進行重構和更改。 Live Unit Testing 在編輯代碼時自動執行全部受影響的測試,確保所作更改不會中斷測試。

  • 指示單元測試是否充分覆蓋代碼,並顯示未被單元測試覆蓋的代碼。 Live Unit Testing 以圖形方式實時描繪代碼覆蓋率,以便一眼就能看到每行代碼覆蓋的測試數,目和未被任何單元測試覆蓋的行。

2)進行實時單元測試:

  1. 在類庫項目中建立一個待測試方法,以下:
/// <summary>
/// 相加(待測試方法)
/// </summary>
/// <param name="num1"></param>
/// <param name="num2"></param>
/// <returns></returns>
public static int Add(int num1, int num2)
{
    return num1 + num2;
}

  2. 根據以上建立單元測試項目的過程,建立一個單元測試項目(測試框架可使用 Live Unit Testing 的 MSTest 測試框架(默認)。 還可以使用 xUnit 和 NUnit 測試框架)。在測試項目添加對被測試類庫項目的引用來提供測試庫訪問權限。

  3. 在測試項目類中,將模板提供的樣本單元測試方法代碼替換爲如下代碼:

[TestMethod()]
[DataRow(10, 20)]
[DataRow(-2, -5)]
[DataRow(1, -2)]
[DataRow(5, null)]
public void Add_Test(int num1, int num2)
{
    Assert.AreEqual(UnitTestClass.Add(num1, num2), num1 + num2);
}

  4. 從頂級 Visual Studio 菜單中依次選擇「測試」 > 「Live Unit Testing」 > 「啓動」 Visual Studio 啓動 Live Unit Testing,使其自動運行全部測試。

   

  5.  完成運行測試後,「測試資源管理器」 顯示總體結果和各個測試的結果。 此外,代碼窗口以圖形方式顯示測試代碼覆蓋率和測試結果。 以下圖所示,三項測試均已成功執行。 它還顯示測試中已覆蓋 Add() 方法中的全部代碼路徑,並已成功執行這些測試(用綠色複選標記「」指示)。 UnitTestClass.cs 中的其餘方法有部分代碼沒有代碼覆蓋率(用藍線「」指示)

  

  還可經過在代碼窗口中選擇一個特定的代碼覆蓋率圖標來得到有關測試覆蓋率和測試結果的更多詳細信息。 若要查看此詳細信息,請執行如下操做:

  單擊行上的綠色複選標記「」, 以下圖所示,Live Unit Testing 指示只有一個測試覆蓋該行的代碼,而且都已成功執行。

  

  Live Unit Testing 中「」標識的主要問題是代碼覆蓋率不完整,能夠經過添加測試方法或改變測試參數,以下圖,能夠看到代碼覆蓋率已擴展到 GetTriangle() 的每一行代碼。 

  在你修改源代碼時,Live Unit Testing 將自動執行新增的和修改後的測試。

  

  6. 處理測試失敗:

  將 Add() 作些許修改,修改成計算兩數相加的絕對值,在保存後Live Unit Testing 指示 Add() 方法執行失敗,以下圖所示:  

  

7. 中止實時單元測試:

  

 

使用 IntelliTest 爲你的代碼生成單元測試

IntelliTest 瀏覽你的 .NET 代碼,以生成測試數據和單元測試套件。 對於代碼中的每一個語句,將生成執行該語句的測試輸入。 爲代碼中的每一個條件分支執行案例分析。 例如,分析 if 語句、斷言和可能引起異常的全部操做。 此分析用於爲你的每一個方法生成參數化單元測試的測試數據,從而建立具備較高代碼覆蓋率的單元測試。

當你運行 IntelliTest 時,你可輕鬆看到哪些測試會失敗,並可添加任何須要的代碼來修復它們。 你可選擇要保存到測試項目中的已生成測試,以提供迴歸套件。 當你更改代碼時,從新運行 IntelliTest,以使生成的測試與你的代碼更改同步。

IntelliTest 僅可用於 C# 且不支持 x64 配置。

IntelliTest 入門

若要生成單元測試,你的類型必須是公共類。 不然,先建立單元測試,而後再生成它們。

  1. 在 Visual Studio 中打開解決方案。 而後打開包含你要測試的方法的類文件。

  2. 在代碼中右鍵單擊一種方法並選擇「建立 IntelliTest」,爲方法中的代碼建立生成單元測試項目。

  

  接受默認格式以生成測試,或更改項目和測試的命名方式。 你能夠建立新的測試項目或將你的測試保存到現有項目。

  

  3. 建立測試項目成功以後,選擇上圖中「運行 IntelliTest」,爲方法中的代碼運行IntelliTest單元測試項目。

  IntelliTest 使用不一樣的輸入屢次運行你的代碼。 每次運行都會在表中表示出來,顯示輸入測試數據以及產生的輸出或異常。

  

  要爲一個類中的全部公共方法生成單元測試,只需右鍵單擊類而不是特定的方法。 而後選擇「運行 IntelliTest」。 使用「瀏覽結果」窗口中的下拉列表,顯示類中每一個方法的單元測試和輸入數據。

  

  對於經過的測試,檢查結果列中報告的結果是否與你對代碼的預期要求匹配。 對於失敗的測試,根據須要修復你的代碼。 而後從新運行 IntelliTest 來驗證修復。

相關文章
相關標籤/搜索