教程:使用 Visual Studio 和 .NET 測試 .NET 類別庫
本教學課程示範如何將測試專案新增至方案,將單元測試自動化。
先決條件
- 本教學課程適用於您在 使用 Visual Studio 建立 .NET 類別庫中建立的解決方案。
建立單元測試專案
單元測試會在開發和發佈期間提供自動化軟體測試。 MSTest 是您可以從中選擇的三個測試架構之一。 其他則是 xUnit 和 nUnit。
啟動 Visual Studio。
開啟您在 使用 Visual Studio建立 .NET 類別庫時所建立的
ClassLibraryProjects
解決方案。將名為 「StringLibraryTest」 的新單元測試專案新增至方案。
在 [方案總管] 中對方案按右鍵,然後選取 [新增>專案]。
在 [[新增專案] 頁面上,於搜尋方塊中輸入 mstest。 從 [語言] 列表中選擇 C# 或 Visual Basic,然後從 [平臺] 清單中選擇 [所有平臺]。
選擇 MSTest 測試項目 樣本,然後選擇 [下一步]。
在 [設定新專案] 頁面上,於 [專案 名稱] 方塊中輸入 StringLibraryTest。 然後選擇 [下一步] 。
在 [其他資訊] 頁面上,選取 [Framework] 方塊中的 [.NET 8]。 然後選擇 建立。
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
單元測試範本所建立的原始程式碼會執行下列動作:
- 它會匯入 Microsoft.VisualStudio.TestTools.UnitTesting 命名空間,其中包含用於單元測試的類型。 在 C# 中,命名空間是透過 GlobalUsings.cs中的
global using
指示詞彙入。 - 它會將 TestClassAttribute 屬性套用至
UnitTest1
類別。 - 它會套用 TestMethodAttribute 屬性以在 C# 中定義
TestMethod1
或在 Visual Basic 中定義TestSub
。
執行單元測試時,會自動執行以
[TestClass] 標記的測試類別中,以 。[TestMethod] 標記的每個方法 - 它會匯入 Microsoft.VisualStudio.TestTools.UnitTesting 命名空間,其中包含用於單元測試的類型。 在 C# 中,命名空間是透過 GlobalUsings.cs中的
新增專案參考
若要讓測試專案與 StringLibrary
類別一起工作,請在 StringLibraryTest 專案中加入一個參考,以將其連結至 StringLibrary
專案。
在
[方案總管] 中,以滑鼠右鍵點擊StringLibraryTest 專案的相依性 節點,然後從操作功能表中選取 [新增專案參考]。 在 [參考管理員] 對話框中,展開 [Projects] 節點,然後選取 StringLibrary旁的方塊。 在編譯 StringLibraryTest 專案時,新增參考至
StringLibrary
元件,讓編譯器能找到 StringLibrary 方法。選取 [確定] 。
新增和執行單元測試方法
當 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 自變數。 或者,您可以在指派給 null
的 string
變數上,呼叫 StartsWithUpper
做為擴充方法。
您將定義三種方法,每個方法都會針對字串陣列中的每個元素呼叫 Assert 方法。 您將呼叫方法多載,讓您指定要在測試失敗時顯示的錯誤訊息。 訊息會識別造成失敗的字串。
若要建立測試方法:
在 [UnitTest1.cs 或 UnitTest1.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)。在功能表欄上,選取 [檔案]>[另存新檔為 UnitTest1.cs] 或 [檔案]>[另存另檔為 UnitTest1.vb] []。 在 [另存新檔] 對話框中,選擇 [儲存] 按鈕旁邊的箭頭,然後選擇 [使用編碼儲存]。
在 確認另存新檔 對話框中,選取 是 按鈕以儲存檔案。
在 [進階儲存選項] 對話框中,從 [編碼] 下拉式清單中選取 [Unicode (UTF-8 with signature) - 代碼頁 65001],然後選取 [確定]。
如果您無法將原始程式碼儲存為UTF8編碼的檔案,Visual Studio可能會將其儲存為 ASCII 檔案。 發生這種情況時,執行環境無法正確解碼超出 ASCII 範圍的 UTF8 字元,因此測試結果將會不正確。
在選單列上,選取 [[測試]>[執行所有測試]。 如果 [測試總管] 視窗
沒有打開,請選擇 [測試] [測試總管] 來開啟它。 這三個測試列在 [通過的測試] 區段中,而 [摘要] 區段會報告測試回合的結果。
處理測試故障或失敗
如果您在進行測試驅動開發(TDD),先撰寫測試,在第一次執行它們時,測試會失敗。 然後將程式代碼新增至讓測試成功的應用程式。 在本教學課程中,您已在撰寫驗證的應用程式程式代碼之後建立測試,因此您沒看到測試失敗。 若要在預期測試失敗時驗證測試失敗,請將無效的值新增至測試輸入。
修改
TestDoesNotStartWithUpper
方法中的words
陣列,以包含字串 “Error”。 您不需要儲存檔案,因為 Visual Studio 會在建置方案以執行測試時,自動儲存已開啟的檔案。string[] words = { "alphabet", "Error", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство", "1234", ".", ";", " " };
Dim words() As String = { "alphabet", "Error", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство", "1234", ".", ";", " " }
從選單列選取 [測試>執行所有測試],以執行測試。 測試總管視窗 顯示兩個測試成功,一個失敗。
具有失敗測試的[測試總管]視窗
選取失敗的測試,
TestDoesNotStartWith
。測試總管 視窗會顯示由斷言產生的訊息:「Assert.IsFalse 失敗。 預期的 'Error': false; 實際的:True"。 由於失敗,陣列中「錯誤」後的字串都未經過測試。
拿掉您在步驟 1 中新增的字串 “Error”。 重新執行測試,並且測試通過了。
測試正式版本的函式庫
現在執行連結庫的偵錯組建時,測試都已通過,接下來再針對連結庫的發行組建執行測試一次。 受多種因素影響,包括編譯器優化,有時候除錯版本和發行版本之間可能會表現出不同的運作方式。
若要測試正式版本:
在 Visual Studio 工具列中,將組建組態從 Debug 變更為 Release。
在 [方案總管]中,按一下右鍵在 StringLibrary 專案上,然後從操作功能表中選取 [建置] 以重新編譯程式庫。
在 StringLibrary 內容功能表中使用建置命令
從功能表列選擇 [測試]>[執行所有測試] 來執行單元測試。 測試通過了。
偵錯測試
如果您使用 Visual Studio 作為 IDE,您可以使用 教學課程:使用 Visual Studio 對 .NET 控制台應用程式進行偵錯, 使用單元測試專案偵錯程式代碼。 不要啟動 ShowCase 應用程式專案,請以滑鼠右鍵點擊 StringLibraryTests 專案,然後從操作功能表中選取 [偵錯測試]。
Visual Studio 會在附加調試程式時啟動測試專案。 執行會在您已新增至測試項目或底層庫代碼的任何斷點停止。
其他資源
- 單元測試基本概念 - Visual Studio
- .NET 中的
單元測試
後續步驟
在本教學課程中,您已經對類別函式庫進行單元測試。 您可以將連結庫發佈至 NuGet 做為套件,讓其他人可以使用連結庫。 若要瞭解如何,請遵循 NuGet 教學課程:
使用 Visual Studio 建立及發佈 NuGet 套件
如果您將連結庫發佈為 NuGet 套件,其他人可以安裝並使用它。 若要瞭解如何,請遵循 NuGet 教學課程:
在 Visual Studio 中安裝和使用套件
函式庫不需要以套件的形式發行。 它可以與使用它的控制台應用程式搭配使用。 若要瞭解如何發佈主控台應用程式,請參閱本系列先前的教學課程:
使用 Visual Studio 發佈 .NET 控制台應用程式