共用方式為


教學:測試 .NET 類別函式庫

本教學課程示範如何將測試專案新增至方案,將單元測試自動化。

先決條件

這個教學是用你在 Create a .NET class library 中建立的解決方案來運作。

建立單元測試專案

單元測試會在開發和發佈期間提供自動化軟體測試。 MSTest 是您可以從中選擇的三個測試架構之一。 其他則是 xUnit 和 nUnit。

  1. 啟動 Visual Studio。

  2. 打開你在 ClassLibraryProjects 中建立的 解決方案。

  3. 將名為 「StringLibraryTest」 的新單元測試專案新增至方案。

    1. 方案總管 中右鍵點選解決方案,選擇 Add>New project

    2. 在 [[新增專案] 頁面上,於搜尋方塊中輸入 mstest。 從語言列表中選擇C#Visual Basic,然後從平台列表中選擇所有平台

    3. 選擇 MSTest 測試項目 樣本,然後選擇 [下一步]。

    4. 在 [設定新專案] 頁面上,於 [專案 名稱] 方塊中輸入 StringLibraryTest。 然後選擇 [下一步]。

    5. Additional information 頁面,選擇 Framework 框中的 .NET 10,選擇 Microsoft.Testing.Platform作為 Test runner,然後選擇 Create

    輸入MSTest測驗專案的額外資訊

  4. Visual Studio 建立專案後,會在程式碼視窗中以以下程式碼開啟類別檔案。 如果未顯示您要使用的語言,請變更頁面頂端的語言選取器。

    namespace StringLibraryTest
    {
    
        [TestClass]
        public sealed class Test1
        {
            [TestMethod]
            public void TestMethod1()
            {
            }
        }
    }
    
    Imports Microsoft.VisualStudio.TestTools.UnitTesting
    
    Namespace StringLibraryTest
        <TestClass>
        Public Class Test1
            <TestMethod>
            Sub TestSub()
    
            End Sub
        End Class
    End Namespace
    

    單元測試範本所建立的原始程式碼會執行下列動作:

    在標有 [TestClass] 的測試類別中,每個標有 [TestMethod] 的方法都會在單元測試執行時自動執行。

  1. 啟動 Visual Studio Code。

  2. 打開你在 ClassLibraryProjects 中建立的 解決方案。

  3. 方案總管 選擇 New Project,或從指令調色盤中選擇 .NET: New Project

  4. 選擇 MSTest 測試專案,命名為「StringLibraryTest」,選擇預設目錄,然後選擇 建立專案。

    專案範本會用以下程式碼建立 StringLibraryTest/Test1.cs :

    namespace StringLibraryTest;
    
    [TestClass]
    public class Test1
    {
        [TestMethod]
        public void TestMethod1()
        {
        }
    }
    

    單元測試範本所建立的原始程式碼會執行下列動作:

    • 應用屬性至類別。
    • 它應用屬性 來定義 。
    • 它匯 入命名空間,該命名空間包含用於單元測試的類型。 命名空間是透過 GlobalUsings.cs 中的指令匯入的。

    在標記為 [TestClass] 的測試類別中,每個標記為 [TestMethod] 的方法,在單元測試呼叫時都會自動執行。

  1. 打開終端機,並導航到 StringLibrary 和 ShowCase 專案的 tutorials 資料夾。

  2. 建立新的 MSTest 測試專案:

    dotnet new mstest -n StringLibraryTest
    

    專案範本會用以下程式碼建立 StringLibraryTest/Test1.cs :

    namespace StringLibraryTest;
    
    [TestClass]
    public class Test1
    {
        [TestMethod]
        public void TestMethod1()
        {
        }
    }
    

    單元測試範本所建立的原始程式碼會執行下列動作:

    • 它會將屬性套用到類別上。
    • 它應用屬性 來定義 。
    • 它匯 入命名空間,該命名空間包含用於單元測試的類型。

    在標記為 [TestClass] 的測試類別中,每個標記為 [TestMethod] 的方法,在單元測試呼叫時都會自動執行。

新增專案參考

為了讓測試專案能與該類別合作,請在專案中加入專案中的參考。

  1. 方案總管 中,右鍵點擊 StringLibraryTest 專案的 Dependencies節點,並從右鍵選單選擇 Add Project Reference

  2. 在 參考管理器 對話框中,選擇 StringLibrary 旁的方塊。

    將 StringLibrary 作為 StringLibraryTest 的專案參考。

  3. 請選擇 [確定]。

  1. 方案總管 右鍵點選「StringLibraryTest」專案,選擇 Add Project Reference

  2. 選擇「StringLibrary」。

  1. 請前往 StringLibraryTest 資料夾並新增專案參考:

    cd StringLibraryTest
    dotnet add reference ../StringLibrary/StringLibrary.csproj
    

新增和執行單元測試方法

當單元測試執行時,類別上有屬性標記的每個方法都會自動執行。 當找到第一個失敗或方法中包含的所有測試成功時,測試方法就會結束。

最常見的測試會呼叫 類別的成員。 許多判斷提示方法至少包含兩個參數,其中一個是預期的測試結果,另一個是實際測試結果。 下表顯示一些 類別最常呼叫的方法:

Assert 方法 功能
Assert.AreEqual 驗證兩個值或物件是否相等。 如果值或物件不相等,斷言就會失敗。
Assert.AreSame 確認兩個物件變數參考相同的物件。 如果變數參考不同的對象,斷言將會失敗。
Assert.IsFalse 驗證條件為 。 如果條件為 ,則斷言失敗。
Assert.IsNotNull 確認物件不是 。 如果物件是 ,則斷言失敗。

你也可以在測試方法中使用該 方法來指示預期拋出的例外類型。 如果未引發指定的例外狀況,測試就會失敗。

在測試這個 方法時,你需要提供若干以大寫字母開頭的字串。 您預期方法會在這些情況下傳回 ,因此您可以呼叫 方法。 同樣地,您需要提供多個字串,這些字串的開頭不是大寫字元。 您預期方法會在這些情況下傳回 ,因此您可以呼叫 方法。

因為你的函式庫方法處理字串,你也要確保它能成功處理 空字串() 和字 串。 空字串是指沒有字元且 其字元為0的字串。 字串是指尚未初始化的字串。 你可以直接以靜態方法呼叫 ,並傳遞單一 參數。 或者你可以將其調用為分配到變數的擴充方法。

你會定義三種方法,每個方法對字串陣列中的每個元素呼叫一個 方法。 你會呼叫一個方法過載,讓你指定一個錯誤訊息,以便在測試失敗時顯示。 訊息會識別造成失敗的字串。

若要建立測試方法:

  1. 在 Test1.cs 或 Test1.vb 代碼視窗中,請將代碼替換為以下代碼:

    using UtilityLibraries;
    
    namespace StringLibraryTest
    {
        [TestClass]
        public sealed class Test1
        {
            [TestMethod]
            public void TestStartsWithUpper()
            {
                // Tests that we expect to return true.
                string[] words = ["Alphabet", "Zebra", "ABC", "Αθήνα", "Москва"];
                foreach (var word in words)
                {
                    bool result = word.StartsWithUpper();
                    Assert.IsTrue(result, $"Expected for '{word}': true; Actual: {result}");
                }
            }
    
            [TestMethod]
            public void TestDoesNotStartWithUpper()
            {
                // Tests that we expect to return false.
                string[] words = ["alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                              "1234", ".", ";", " "];
                foreach (var word in words)
                {
                    bool result = word.StartsWithUpper();
                    Assert.IsFalse(result, $"Expected for '{word}': false; Actual: {result}");
                }
            }
    
            [TestMethod]
            public void DirectCallWithNullOrEmpty()
            {
                // Tests that we expect to return false.
                string?[] words = [string.Empty, null];
                foreach (var word in words)
                {
                    bool result = StringLibrary.StartsWithUpper(word);
                    Assert.IsFalse(result, $"Expected for '{word ?? "<null>"}': false; Actual: {result}");
                }
            }
        }
    }
    
    Imports Microsoft.VisualStudio.TestTools.UnitTesting
    Imports UtilityLibraries
    
    Namespace StringLibraryTest
        <TestClass>
        Public Class UnitTest1
            <TestMethod>
            Public Sub TestStartsWithUpper()
                ' Tests that we expect to return true.
                Dim words() As String = {"Alphabet", "Zebra", "ABC", "Αθήνα", "Москва"}
                For Each word In words
                    Dim result As Boolean = word.StartsWithUpper()
                    Assert.IsTrue(result,
                           $"Expected for '{word}': true; Actual: {result}")
                Next
            End Sub
    
            <TestMethod>
            Public Sub TestDoesNotStartWithUpper()
                ' Tests that we expect to return false.
                Dim words() As String = {"alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                                   "1234", ".", ";", " "}
                For Each word In words
                    Dim result As Boolean = word.StartsWithUpper()
                    Assert.IsFalse(result,
                           $"Expected for '{word}': false; Actual: {result}")
                Next
            End Sub
    
            <TestMethod>
            Public Sub DirectCallWithNullOrEmpty()
                ' Tests that we expect to return false.
                Dim words() As String = {String.Empty, Nothing}
                For Each word In words
                    Dim result As Boolean = StringLibrary.StartsWithUpper(word)
                    Assert.IsFalse(result,
                           $"Expected for '{If(word Is Nothing, "<null>", word)}': false; Actual: {result}")
                Next
            End Sub
        End Class
    End Namespace
    

    方法中大寫字元的測試包括希臘大寫字母 "alpha" (U+0391) 和西里爾大寫字母 "EM" (U+041C)。 方法中小寫字元的測試包括希臘小字母 Alpha (U+03B1) 和斯拉夫小字母 Ghe (U+0433)。

  2. 在選單列中,選擇「檔案另存為Test1.cs」或「檔案另存為Test1.vb」。 在 [另存新檔] 對話框中,選擇 [儲存] 按鈕旁邊的箭頭,然後選擇 [使用編碼儲存]。

  3. 在 確認另存新檔 對話框中,選取 是 按鈕以儲存檔案。

  4. 在 [進階儲存選項] 對話框中,從 [編碼] 下拉式清單中選取 [Unicode (UTF-8 with signature) - 代碼頁 65001],然後選取 [確定]。

    如果你沒有把原始碼存成 UTF8 編碼的檔案,Visual Studio 可能會把它存成 ASCII 檔案。 發生這種情況時,運行時間無法準確解碼 ASCII 範圍之外的 UTF8 字元,並且測試結果不正確。

  5. 在選單列上,選取 [[測試][執行所有測試]。 如果 [測試總管] 視窗 沒有打開,請選擇 [測試][測試總管]來開啟它。 這三個測試列在 [通過的測試] 區段中,而 [摘要] 區段會報告測試回合的結果。

    測試總管視窗顯示通過的測試

  1. 打開 StringLibraryTest/Test1.cs ,並將所有程式碼替換為以下程式碼。

    using UtilityLibraries;
    
    namespace StringLibraryTest
    {
        [TestClass]
        public sealed class Test1
        {
            [TestMethod]
            public void TestStartsWithUpper()
            {
                // Tests that we expect to return true.
                string[] words = ["Alphabet", "Zebra", "ABC", "Αθήνα", "Москва"];
                foreach (var word in words)
                {
                    bool result = word.StartsWithUpper();
                    Assert.IsTrue(result, $"Expected for '{word}': true; Actual: {result}");
                }
            }
    
            [TestMethod]
            public void TestDoesNotStartWithUpper()
            {
                // Tests that we expect to return false.
                string[] words = ["alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                              "1234", ".", ";", " "];
                foreach (var word in words)
                {
                    bool result = word.StartsWithUpper();
                    Assert.IsFalse(result, $"Expected for '{word}': false; Actual: {result}");
                }
            }
    
            [TestMethod]
            public void DirectCallWithNullOrEmpty()
            {
                // Tests that we expect to return false.
                string?[] words = [string.Empty, null];
                foreach (var word in words)
                {
                    bool result = StringLibrary.StartsWithUpper(word);
                    Assert.IsFalse(result, $"Expected for '{word ?? "<null>"}': false; Actual: {result}");
                }
            }
        }
    }
    

    方法中大寫字元的測試包括希臘大寫字母 "alpha" (U+0391) 和西里爾大寫字母 "EM" (U+041C)。 方法中小寫字元的測試包括希臘小字母 Alpha (U+03B1) 和斯拉夫小字母 Ghe (U+0433)。

  2. 儲存您的變更。

建置並執行你的測試

  1. 方案總管 中,右鍵點選解決方案並選擇 Build,或在指令面板中選擇 .NET: Build

  2. 選擇 測試 視窗,選擇 執行測試 ,或從指令面板中選擇 測試:執行所有測試。

    Visual Studio Code 測試探索器

  1. 開啟 StringLibraryTest/Test1.cs ,並用以下程式碼替換所有程式碼:

    using UtilityLibraries;
    
    namespace StringLibraryTest
    {
        [TestClass]
        public sealed class Test1
        {
            [TestMethod]
            public void TestStartsWithUpper()
            {
                // Tests that we expect to return true.
                string[] words = ["Alphabet", "Zebra", "ABC", "Αθήνα", "Москва"];
                foreach (var word in words)
                {
                    bool result = word.StartsWithUpper();
                    Assert.IsTrue(result, $"Expected for '{word}': true; Actual: {result}");
                }
            }
    
            [TestMethod]
            public void TestDoesNotStartWithUpper()
            {
                // Tests that we expect to return false.
                string[] words = ["alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                              "1234", ".", ";", " "];
                foreach (var word in words)
                {
                    bool result = word.StartsWithUpper();
                    Assert.IsFalse(result, $"Expected for '{word}': false; Actual: {result}");
                }
            }
    
            [TestMethod]
            public void DirectCallWithNullOrEmpty()
            {
                // Tests that we expect to return false.
                string?[] words = [string.Empty, null];
                foreach (var word in words)
                {
                    bool result = StringLibrary.StartsWithUpper(word);
                    Assert.IsFalse(result, $"Expected for '{word ?? "<null>"}': false; Actual: {result}");
                }
            }
        }
    }
    

    方法中大寫字元的測試包括希臘大寫字母 "alpha" (U+0391) 和西里爾大寫字母 "EM" (U+041C)。 方法中小寫字元的測試包括希臘小字母 Alpha (U+03B1) 和斯拉夫小字母 Ghe (U+0433)。

  2. 儲存你的變更並執行測試:

    dotnet test
    

    測試應該會通過。

處理測試故障或失敗

如果您在進行測試驅動開發(TDD),先撰寫測試,在第一次執行它們時,測試會失敗。 然後將程式代碼新增至讓測試成功的應用程式。 在本教學課程中,您已在撰寫驗證的應用程式程式代碼之後建立測試,因此您沒看到測試失敗。 若要在預期測試失敗時驗證測試失敗,請將無效的值新增至測試輸入。

  1. 修改 方法中的 陣列,以包含字串 “Error”。

    string[] words = { "alphabet", "Error", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                       "1234", ".", ";", " " };
    
    Dim words() As String = { "alphabet", "Error", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                       "1234", ".", ";", " " }
    
    
  1. 從選單列選取 [測試執行所有測試],以執行測試。 測試總管視窗 顯示兩個測試成功,一個失敗。

    具有失敗測試的[測試總管]視窗

  2. 選擇失敗的測試,。

    測試總管 視窗會顯示由斷言產生的訊息:「Assert.IsFalse 失敗。 預期 'Error': false;實際:沒錯。由於失敗,未測試「錯誤」之後陣列中的字串。

    [測試總管] 視窗,其中顯示 IsFalse 斷言失敗

  1. 在編輯器中點擊測試旁邊的綠色錯誤點來執行測試。

    輸出顯示測試失敗,並會顯示失敗測試的錯誤訊息:「Assert.IsFalse 失敗。 預期的「錯誤」:假;實際:正確」。 由於失敗,陣列中「錯誤」之後的字串未被測試。

    Visual Studio Code 考試不及格

  1. 執行測試:

    dotnet test
    

    輸出顯示測試失敗,並會顯示失敗測試的錯誤訊息:「Assert.IsFalse 失敗。 預期的「錯誤」:假;實際:正確」。 由於失敗,陣列中「錯誤」之後的字串未被測試。

  1. 移除你新增的字串「Error」。

  2. 重新執行測試,並且測試通過了。

測試正式版本的函式庫

現在所有測試都通過了,執行函式庫的除錯建置時,再用該函式庫的 Release 建置再執行一次測試。 包括編譯器優化在內的多種因素,有時會在除錯與發佈版本之間產生不同的行為。

若要測試正式版本:

  1. 在Visual Studio工具列中,將建置設定從 Debug 改為 Release

  2. 方案總管 中,右鍵點擊 StringLibrary 專案,並從右鍵選單選擇 Build 以重新編譯函式庫。

  3. 從功能表列選擇 [測試][執行所有測試] 來執行單元測試。 測試通過了。

用 Release 建置設定執行測試:

dotnet test StringLibraryTest/StringLibraryTest.csproj --configuration Release

測試通過了。

用 Release 建置設定執行測試:

dotnet test --configuration Release

測試通過了。

偵錯測試

如果你用 Visual Studio 作為 IDE,可以用 Tutorial: Debug a .NET console application 中的流程來用單元測試專案除錯程式碼。 不要啟動 ShowCase 應用程式專案,請以滑鼠右鍵點擊 StringLibraryTests 專案,然後從操作功能表中選取 [偵錯測試]。

Visual Studio 啟動測試專案時會附帶除錯器。 執行會在您新增至測試專案或基礎程式庫程式碼的任何中斷點停止。

如果你用 Visual Studio Code 作為 IDE,可以用 Debug a .NET console application 中展示的流程,來用單元測試專案除錯程式碼。 不要啟動 ShowCase 應用程式專案,打開 StringLibraryTest/Test1.cs,並在當前檔案第 7 行到 8 行之間選擇除 錯測試 。 如果找不到,按 CtrlShiftP 開啟指令面板並進入 Reload 視窗。

Visual Studio Code 會以附帶除錯器啟動測試專案。 執行會在你加入測試專案或底層函式庫程式碼的任何中斷點停止。

其他資源

清理資源

GitHub 會在你 30 天不活躍後自動刪除你的 Codespace。 如果你打算繼續探索這個系列的更多教學,可以讓你的 Codespace 保持配置。 如果你準備好造訪 .NET 網站下載 .NET SDK,你可以刪除你的 Codespace。 要刪除你的 Codespace,打開瀏覽器視窗並導航到 你的 Codespaces。 你會在視窗中看到你的代碼空間清單。 在學習教學程式碼空間中選擇三個點()。 然後選擇「刪除」。

後續步驟

在本教學課程中,您已經對類別函式庫進行單元測試。 你可以透過將函式庫以套件形式發佈到 NuGet 給其他人使用。 若要瞭解如何,請遵循 NuGet 教學課程:

使用 dotnet CLI 建立並發佈套件

如果您將連結庫發佈為 NuGet 套件,其他人可以安裝並使用它。 若要瞭解如何,請遵循 NuGet 教學課程:

使用 dotnet CLI 安裝並使用套件

函式庫不需要以套件的形式發行。 它可以與使用它的控制台應用程式搭配使用。 若要瞭解如何發佈主控台應用程式,請參閱本系列先前的教學課程: