共用方式為


教程:使用 Visual Studio 和 .NET 測試 .NET 類別庫

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

先決條件

建立單元測試專案

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

  1. 啟動 Visual Studio。

  2. 開啟您在 使用 Visual Studio建立 .NET 類別庫時所建立的 ClassLibraryProjects 解決方案。

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

    1. [方案總管] 中對方案按右鍵,然後選取 [新增>專案]

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

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

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

    5. 在 [其他資訊] 頁面上,選取 [Framework] 方塊中的 [.NET 8]。 然後選擇 建立

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

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

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

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

新增專案參考

若要讓測試專案與 StringLibrary 類別一起工作,請在 StringLibraryTest 專案中加入一個參考,以將其連結至 StringLibrary 專案。

  1. [方案總管]中,以滑鼠右鍵點擊 StringLibraryTest 專案的 相依性 節點,然後從操作功能表中選取 [新增專案參考]

  2. 在 [參考管理員] 對話框中,展開 [Projects] 節點,然後選取 StringLibrary旁的方塊。 在編譯 StringLibraryTest 專案時,新增參考至 StringLibrary 元件,讓編譯器能找到 StringLibrary 方法。

  3. 選取 [確定]

新增和執行單元測試方法

當 Visual Studio 執行單元測試時,它會在以 TestClassAttribute 屬性標示的類別中,執行以 TestMethodAttribute 屬性標記的每個方法。 當找到第一個失敗,或方法中包含的所有測試都成功時,測試方法就會結束。

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

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

您也可以在測試方法中使用 Assert.ThrowsException 方法來指出預期擲回的例外狀況類型。 如果未擲回指定的例外狀況,測試就會失敗。

在測試 StringLibrary.StartsWithUpper 方法時,您想要提供以大寫字元開頭的字串數目。 您預期方法會在這些情況下傳回 true,因此您可以呼叫 Assert.IsTrue 方法。 同樣地,您想要提供一些以非大寫字元開頭的字串。 您預期方法會在這些情況下傳回 false,因此您可以呼叫 Assert.IsFalse 方法。

因為連結庫方法會處理字串,所以您也想要確定它已成功處理 空字串串 (String.Empty、沒有字元且其 Length 為 0 的有效字串,以及尚未初始化的 null 字串。 您可以直接呼叫 StartsWithUpper 做為靜態方法,並傳遞單一 String 自變數。 或者,您可以在指派給 nullstring 變數上,呼叫 StartsWithUpper 做為擴充方法。

您將定義三種方法,每個方法都會針對字串陣列中的每個元素呼叫 Assert 方法。 您將呼叫方法多載,讓您指定要在測試失敗時顯示的錯誤訊息。 訊息會識別造成失敗的字串。

若要建立測試方法:

  1. 在 [UnitTest1.csUnitTest1.vb 程式代碼] 視窗中,以下列程式代碼取代程序代碼:

    using UtilityLibraries;
    
    namespace StringLibraryTest
    {
        [TestClass]
        public class UnitTest1
        {
            [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,
                           string.Format("Expected for '{0}': true; Actual: {1}",
                                         word, 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,
                           string.Format("Expected for '{0}': false; Actual: {1}",
                                         word, 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,
                           string.Format("Expected for '{0}': false; Actual: {1}",
                                         word == null ? "<null>" : word, 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
    

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

  2. 在功能表欄上,選取 [檔案]>[另存新檔為 UnitTest1.cs] 或 [檔案]>[另存另檔為 UnitTest1.vb] []。 在 [另存新檔] 對話框中,選擇 [儲存] 按鈕旁邊的箭頭,然後選擇 [使用編碼儲存]

    Visual Studio [另存新檔] 對話框

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

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

    Visual Studio [進階儲存選項] 對話框

    如果您無法將原始程式碼儲存為UTF8編碼的檔案,Visual Studio可能會將其儲存為 ASCII 檔案。 發生這種情況時,執行環境無法正確解碼超出 ASCII 範圍的 UTF8 字元,因此測試結果將會不正確。

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

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

處理測試故障或失敗

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

  1. 修改 TestDoesNotStartWithUpper 方法中的 words 陣列,以包含字串 “Error”。 您不需要儲存檔案,因為 Visual Studio 會在建置方案以執行測試時,自動儲存已開啟的檔案。

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

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

  3. 選取失敗的測試,TestDoesNotStartWith

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

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

  4. 拿掉您在步驟 1 中新增的字串 “Error”。 重新執行測試,並且測試通過了。

測試正式版本的函式庫

現在執行連結庫的偵錯組建時,測試都已通過,接下來再針對連結庫的發行組建執行測試一次。 受多種因素影響,包括編譯器優化,有時候除錯版本和發行版本之間可能會表現出不同的運作方式。

若要測試正式版本:

  1. 在 Visual Studio 工具列中,將組建組態從 Debug 變更為 Release

    Visual Studio 工具列,其中「發行組建」已被醒目提示

  2. [方案總管]中,按一下右鍵在 StringLibrary 專案上,然後從操作功能表中選取 [建置] 以重新編譯程式庫。

    在 StringLibrary 內容功能表中使用建置命令

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

偵錯測試

如果您使用 Visual Studio 作為 IDE,您可以使用 教學課程:使用 Visual Studio 對 .NET 控制台應用程式進行偵錯, 使用單元測試專案偵錯程式代碼。 不要啟動 ShowCase 應用程式專案,請以滑鼠右鍵點擊 StringLibraryTests 專案,然後從操作功能表中選取 [偵錯測試]

Visual Studio 會在附加調試程式時啟動測試專案。 執行會在您已新增至測試項目或底層庫代碼的任何斷點停止。

其他資源

後續步驟

在本教學課程中,您已經對類別函式庫進行單元測試。 您可以將連結庫發佈至 NuGet 做為套件,讓其他人可以使用連結庫。 若要瞭解如何,請遵循 NuGet 教學課程:

使用 Visual Studio 建立及發佈 NuGet 套件

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

在 Visual Studio 中安裝和使用套件

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

使用 Visual Studio 發佈 .NET 控制台應用程式