分享方式:


單元測試基本概念

藉由建立及執行單元測試,檢查您的程式碼是否如預期般執行。 這之所以稱為單元測試,是因為您將程式功能分解成離散的可測試行為,這些行為能作為個別的「單位」加以測試。 Visual Studio [測試總管] 提供富彈性又有效率的方式來執行單元測試,並可在 Visual Studio 中檢視結果。 Visual Studio 會安裝 Managed 程式碼和原生程式碼適用的 Microsoft 單元測試架構。 請使用 「單元測試架構」 (unit testing framework) 來建立單元測試並加以執行,然後報告這些測試的結果。 當您進行變更來測試程式碼是否仍正常運作時,請重新執行單元測試。 Visual Studio Enterprise 可透過 Live Unit Testing 自動執行這項作業,該工具會偵測受到程式碼變更影響的測試,並在您鍵入時於背景執行這些測試。

當單元測試是軟體開發工作流程中不可或缺的一部分時,就能對您的程式碼品質發揮最大的作用。 一旦您撰寫函式或其他應用程式程式碼區塊,就會建立單元測試,以便驗證該程式碼的行為是否對應於輸入資料的標準、界限和不正確情況,並檢查程式碼所做的任何明確或隱含假設。 藉由 「測試驅動式開發」(test driven development),您可在撰寫程式碼之前先建立單元測試,以便將單元測試做為設計文件和功能規格。

[測試總管] 也可以執行在其中已實作 [測試總管] 附加元件介面的協力廠商和開放原始碼的單元測試架構。 您可以透過 Visual Studio 擴充功能管理員和 Visual Studio 組件庫加入多個這些架構。 如需詳細資訊,請參閱安裝協力廠商單元測試架構

您可以從您的程式碼快速產生測試專案和測試方法,或在您需要的時候以手動方式建立此測試。 當您使用 IntelliTest 來探索 .NET 程式碼時,可以產生測試資料和單元測試套件。 其會為程式碼中的每一個陳述式產生一個用以執行該陳述式的測試輸入。 了解如何 產生 .NET 程式碼的單元測試

開始使用

如需單元測試的簡介以便直接參考編碼,請參閱下列其中一個文章:

The Bank 解決方案範例

在本文中,我們會使用稱為 MyBank 的虛構應用程式開發來作為範例。 您不需要實際程式碼來照著本文章中的說明進行。 測試方法是以 C# 所撰寫並使用 Microsoft 受控程式碼單元測試架構呈現。 不過,很容易就可以將概念移轉到其他語言與架構。

MyBank 解決方案 2019

MyBank 解決方案 2022

我們第一次在 MyBank 應用程式的設計嘗試包括帳戶元件 (代表個別帳戶及其與銀行的交易),和資料庫元件 (代表可彙總及管理個別帳戶的功能)。

我們建立的 Bank 方案包含兩個專案:

  • Accounts

  • BankDB

我們第一次在 Accounts 專案的設計嘗試包含一個保留帳戶相關基本資訊的類別、一個指定任一種帳戶通用功能 (例如在帳戶的資產中存款和提款) 的介面,以及一個從該介面衍生代表支票帳戶的類別。 一開始我們先在帳戶專案中建立下列原始程式檔:

  • AccountInfo.cs 定義帳戶的基本資訊。

  • IAccount.cs 定義帳戶的標準 IAccount 介面,包括從帳戶資產存款和提款,以及擷取帳戶餘額的方法。

  • CheckingAccount.cs 包含的 CheckingAccount 類別可實作支票帳戶的 IAccount 介面。

我們從經驗中知道一件事,那就是從支票帳戶提款必須先確認提取的金額小於帳戶餘額。 因此我們使用一個可檢查此條件的方法來覆寫 IAccount.Withdraw 中的 CheckingAccount 方法。 此方法可能看起來像這樣:

public void Withdraw(double amount)
{
    if(m_balance >= amount)
    {
        m_balance -= amount;
    }
    else
    {
        throw new ArgumentException(nameof(amount), "Withdrawal exceeds balance!");
    }
}

現在我們已經有一些程式碼,該開始測試了。

使用 Copilot 建立單元測試

您也可以使用 Copilot /tests Slash 命令,從程式碼產生單元測試。 例如,您可以輸入 /tests using NUnit Framework 以產生 NUnit 測試。 如需詳細資訊,請參閱在 Copilot Chat 使用 Slash 命令

建立單元測試專案和測試方法 (C#)

針對 C#,從您的程式碼中產生單元測試專案和單元測試虛設常式通常較快。 或者您可以視您的需求而定,選擇以手動方式建立單元測試專案和測試。 如果您想要使用協力廠商架構從程式碼建立單元測試,您需要安裝下列其中一個延伸模組: NUnitxUnit。 如果您未使用 C#,請略過本章節並移至 手動建立單元測試專案和單元測試

產生單元測試專案和單元測試虛設常式

  1. 在程式碼編輯器視窗中,以滑鼠右鍵按一下並從右鍵功能表中選擇 [建立單元測試]

    從編輯器視窗,檢視內容功能表

    注意

    [建立單元測試] 功能表命令僅適用於 C# 程式碼。 若要搭配 .NET Core 或 .NET Standard 使用此方法,則需要 Visual Studio 2019 或更新版本。

    從編輯器視窗,檢視內容功能表

    注意

    [建立單元測試] 功能表命令僅適用於 C# 程式碼。 若要搭配 .NET Core 或 .NET Standard 使用此方法,則需要 Visual Studio 2019 或更新版本。

  2. 選擇 [確定] 接受預設值來建立單元測試,或變更過去用來建立和命名單元測試專案和單元測試的值。 您可以選取預設加入此單元測試方法的程式碼。

    Visual Studio 的 [建立單元測試] 對話方塊

    Visual Studio 的 [建立單元測試] 對話方塊

  3. 會針對此類別中的所有方法,在新的單元測試專案中建立單元測試虛設常式。

    隨即建立單元測試

    隨即建立單元測試

  4. 現在往前跳至了解如何 撰寫測試 好讓您的單元測試有意義,以及您可能會想加入的任何額外單元測試以徹底測試程式碼。

手動建立單元測試專案與單元測試

單元測試專案通常會反映單一程式碼專案的結構。 在 MyBank 範例中,您可以將名為 AccountsTestsBankDbTests 的兩個單元測試專案加入 Bank 方案中。 這些測試專案名稱可隨意命名,不過最好採用標準命名慣例。

將單元測試專案加入方案:

  1. 方案總管中,以滑鼠右鍵按一下解決方案,然後選擇 [新增]>[新的專案]

  2. 在專案範本搜尋方塊中輸入 [測試],以尋找您想要使用之測試架構的單元測試專案範本。 (本文章的範例中,我們會使用 MSTest。)

  3. 請在下一頁命名專案。 若要測試本範例的 Accounts 專案,您可以將專案命名為 AccountsTests

  4. 在您的單元測試專案中,可在本例中將受測程式碼專案的參考加入帳戶專案。

    建立程式碼專案的參考:

    1. 在 [方案總管] 的單元測試中,以滑鼠右鍵按一下 [參考][相依性] 節點,然後選擇 [新增專案參考][新增參考] (以可用的為準)。

    2. 在 [參考管理員] 對話方塊上,開啟 [方案] 節點,然後選擇 [專案]。 選取程式碼專案名稱,然後關閉對話方塊。

每個單元測試專案包含的類別都可反映程式碼專案中的類別名稱。 在本例中, AccountsTests 專案可能包含下列類別:

  • AccountInfoTests 類別包含 AccountInfo 專案中 Accounts 類別的單元測試方法。

  • CheckingAccountTests 類別包含 CheckingAccount 類別的單元測試方法。

撰寫您的測試

您使用的單元測試架構和 Visual Studio IntelliSense 會引導您完成撰寫程式碼專案的單元測試程式碼。 若要在 [測試總管] 中執行,大部分的架構都會要求您新增特定屬性,以識別單元測試方法。 這些架構也會提供一個辨別測試方法是否通過或失敗的方式,通常是透過判斷提示陳述式或方法屬性。 其他屬性會識別在類別初始化和每個測試方法之前的選用設定方法,和識別在每個測試方法之後和終結類別之前的清除方法。

AAA (排列、作用、判斷提示) 模式是為受測方法撰寫單元測試的常見方式。

  • 單元測試方法的 [排列] 區段會初始化物件,並為傳遞至受測方法的資料設定值。

  • [作用] 區段會叫用含有所排列參數的受測方法。

  • [判斷提示] 區段會驗證受測方法的動作是否如預期。 針對 .NET,Assert 類別中的方法通常用於驗證。

若要測試本範例的 CheckingAccount.Withdraw 方法,我們可以撰寫兩個測試: 一個是驗證方法的標準行為,另一個測試會驗證超過餘額的取款將會失敗 (下列程式碼顯示 .NET 中支援的 MSTest 單元測試)。 在 CheckingAccountTests 類別中,我們會加入下列方法:

[TestMethod]
public void Withdraw_ValidAmount_ChangesBalance()
{
    // arrange
    double currentBalance = 10.0;
    double withdrawal = 1.0;
    double expected = 9.0;
    var account = new CheckingAccount("JohnDoe", currentBalance);

    // act
    account.Withdraw(withdrawal);

    // assert
    Assert.AreEqual(expected, account.Balance);
}

[TestMethod]
public void Withdraw_AmountMoreThanBalance_Throws()
{
    // arrange
    var account = new CheckingAccount("John Doe", 10.0);

    // act and assert
    Assert.ThrowsException<System.ArgumentException>(() => account.Withdraw(20.0));
}

如需 Microsoft 單元測試架構的詳細資訊,請參閱下列其中一個文章:

設定單元測試逾時

如果您使用 MSTest 架構,您可以使用 TimeoutAttribute 在個別的測試方法上設定逾時:

[TestMethod]
[Timeout(2000)]  // Milliseconds
public void My_Test()
{ ...
}

設定允許逾時上限:

[TestMethod]
[Timeout(TestTimeout.Infinite)]  // Milliseconds
public void My_Test ()
{ ...
}

在 [測試總管] 中執行測試

在建置測試專案後,這些測試便會出現在 [測試總管] 中。 如果看不到 [測試總管],請選擇 Visual Studio 功能表上的 [測試],選擇 [Windows],然後選擇 [測試總管] (或按一下 [Ctrl] + [E], [T])。

單元測試總管

單元測試總管

在您執行、寫入和重新執行您的測試時,[測試總管] 可以顯示 [失敗的測試]、[通過的測試]、[略過的測試] 及 [未執行的測試] 群組中的結果。 您可以在工具列中以不同選項來選擇不同的群組。

在搜尋方塊中找出全域層級中相符的文字或選取其中一個預先定義的篩選器,也能在任何檢視中篩選測試。 您可以隨時執行測試的任何選取範圍。 測試回合的結果會立即顯示在 [總管] 視窗上方的通過/失敗列中。 選取測試時,會顯示測試方法結果的詳細資料。

執行測試並加以檢視

[測試總管] 工具列可協助您探索、組織和執行您有興趣的測試。

從 [測試總管] 的工具列執行測試

從 [測試總管] 的工具列執行測試

您可以選擇 [全部執行] (或按一下 [Ctrl] + [R][V]), 以執行所有測試,或選擇 [執行] 以選擇要執行的測試子集 ([Ctrl] + [R][T])。 選取測試來在測試詳細資料窗格中檢視該測試的詳細資料。 從滑鼠右鍵功能表選擇 [開啟測試] (鍵盤: [F12]) 以顯示所選測試的原始程式碼。

如果個別測試沒有任何會防止它們依任意順序執行的相依性,請使用工具列的設定功能表開啟平行測試執行。 這可大幅縮短執行所有測試所需的時間。

每次建置後執行測試

若要在每次進行本機建置之後執行單元測試,請開啟 [測試總管] 中的設定圖示,然後選取 [建置之後執行測試]

篩選與群組測試清單

當您有大量的測試時,您可以在 [測試總管] 搜尋方塊中鍵入,以使用指定字串來篩選清單。 您可以從篩選清單中選擇以進一步限制篩選事件。

搜尋篩選條件分類

搜尋篩選條件分類

Button 描述
[測試總管] 的 [群組] 按鈕 若要依分類將測試分組,請選擇 [群組依據] 按鈕。

如需詳細資訊,請參閱使用測試總管執行單元測試

Q&A

問:如何偵錯單元測試?

答: 您可以使用 [測試總管] 來啟動測試的偵錯工作階段。 使用 Visual Studio 偵錯工具逐步執行程式碼可讓您順暢地在單元測試和受測專案之間來回進行。 啟動偵錯:

  1. 在 Visual Studio 編輯器中,於您要偵錯的一個或多個測試方法中設定中斷點。

    注意

    由於測試方法可以依照任何順序執行,請在您要偵錯的所有測試方法中設定中斷點。

  2. 在 [測試總管] 中選取測試方法,然後從捷徑功能表中選擇 [偵錯選取的測試]

進一步了解 偵錯單元測試的詳細資料。

問:如果我使用 TDD,要如何從我的測試產生程式碼?

答案: 請使用 [快速動作] 來在您的專案程式碼中產生類別和方法。 在測試方法中撰寫呼叫您希望產生類別或方法的陳述式,然後開啟出現在錯誤下方的燈泡。 若呼叫是針對新類別的建構函式,請從功能表中選擇 [產生型別],然後遵循精靈來在您的程式碼專案中插入類別。 若呼叫針對方法,請從 IntelliSense 功能表中選擇 [產生方法]

產生方法虛設常式快速動作功能表

產生方法虛設常式快速動作功能表

問:是否可以建立採用多個資料集做為輸入來執行測試的單元測試?

A: 可以。 「資料驅動型測試方法」 (data-driven test method) 可讓您使用單一單元測試方法測試某個範圍的值。 請使用此測試方法的 DataRowDynamicDataDataSource 屬性指定包含您要測試之變數值的資料來源。

資料來源中每個資料列會執行一次屬性的方法。 如果任何反覆項目失敗,[測試總管] 會報告該方法的測試失敗。 該方法的測試結果詳細資料窗格會向您顯示每個資料列通過/失敗狀態的方法。

深入了解 資料驅動的單元測試

問:是否可以檢視我的單元測試已測試了多少程式碼?

A: 可以。 您可以使用 Visual Studio Enterprise 中的 Visual Studio 程式碼涵蓋範圍工具,來判斷單元測試實際測試的程式碼數量。 原生和 Managed 語言,以及可由單元測試架構執行的所有單元測試架構都受支援。

您可以在方案中的所選測試或所有測試上執行程式碼涵蓋範圍。 [程式碼涵蓋範圍結果] 視窗會顯示線條、函式、類別、命名空間及模組所運用的產品程式碼區塊的百分比。

若要在解決方案中執行測試方法的程式碼涵蓋範圍,請選擇 [測試]>[為所有測試分析程式碼涵蓋範圍]

涵蓋範圍結果會出現在 [程式碼涵蓋範圍結果] 視窗中。

程式碼涵蓋範圍結果

程式碼涵蓋範圍結果

進一步了解 程式碼涵蓋範圍

問:是否可以測試我程式碼中具有外部相依性的方法?

A: 可以。 如果您有 Visual Studio Enterprise,則 Microsoft Fakes 可用於您透過 Managed 程式碼適用的單元測試架構所撰寫的測試方法。

Microsoft Fakes 會使用兩種方式來建立外部相依性的替代類別:

  1. 「虛設常式」 (stub) 會產生自目標相依性類別的父介面衍生的替代類別。 虛設常式方法可以取代為目標類別的公用虛擬方法。

  2. 「填充碼」 (shim) 會使用執行階段檢測將呼叫轉向至目標方法,以使用填充碼方法來替代非虛擬方法。

在這兩種方法中,您可以對相依性方法使用呼叫所產生的委派,以指定您要讓測試方法執行的行為。

深入了解 使用 Microsoft Fakes 隔離單元測試方法

問:是否可以使用其他單元測試架構搭配 IntelliTest?

答: 可以,請遵循 尋找並安裝其他架構中的步驟。 重新啟動 Visual Studio 之後,重新開啟方案以建立您的單元測試,然後在這裡選取您已安裝的架構:

選取其他已安裝的單元測試架構

將會使用所選擇的架構來建立您的單元測試虛設常式。

問:如何匯出單元測試結果?

答:您可以使用 .runsettings 檔案搭配命令列或 Visual Studio IDE 來設定單元測試,並設定測試結果檔案。 如需詳細資訊,請參閱 LoggerRunSettings 項目