Live Unit Testing 的使用者入門
在 Visual Studio 方案中啟用 Live Unit Testing 時,其會以視覺化方式說明測試涵蓋範圍和測試狀態。 每當您修改程式碼時,Live Unit Testing 也會動態執行測試,並在變更導致測試失敗時隨即通知您。
Live Unit Testing 可以用來測試目標設為 .NET Framework、.NET Core 或 .NET 5+ 的方案。 在本教學課程中,您將學習如何建立目標設為 .NET 的簡單類別庫來使用 Live Unit Testing,而且您將建立目標設為 .NET 的 MSTest 專案來進行測試。
您可以從 GitHub 的 MicrosoftDocs/visualstudio-docs 存放庫下載完整 C# 方案。
必要條件
本教學課程要求您先安裝具有 .NET 桌面開發工作負載的 Visual Studio Enterprise Edition。
建立方案和類別庫專案
從建立名為 UtilityLibraries 的 Visual Studio 方案開始,此方案包含單一 .NET 類別庫專案 StringLibrary。
方案只是一或多個專案的容器。 若要建立空白方案,請開啟 Visual Studio 並執行下列作業:
從頂層 Visual Studio 功能表中,依序選取 [檔案]>、[新增]>[專案]。
在範本搜尋方塊中鍵入 [方案],然後選取 [空白方案] 範本。 將專案命名 UtilityLibraries。
完成建立方案。
既然您已建立方案,您將建立名為 StringLibrary 的類別庫,其中包含可處理字串的許多擴充方法。
在 [方案總管] 中,以滑鼠右鍵按一下 UtilityLibraries 方案並選取 [新增]>[新增專案]。
在範本的搜尋方塊中鍵入類別庫,然後選取目標設為 .NET 或 .NET Standard 的 [類別庫] 範本。 按一下 [下一步] 。
將專案命名為 StringLibrary。
按一下 [建立] 以建立專案。
將程式碼編輯器中的所有現有程式碼都取代為下列程式碼:
using System; namespace UtilityLibraries { public static class StringLibrary { public static bool StartsWithUpper(this string s) { if (String.IsNullOrWhiteSpace(s)) return false; return Char.IsUpper(s[0]); } public static bool StartsWithLower(this string s) { if (String.IsNullOrWhiteSpace(s)) return false; return Char.IsLower(s[0]); } public static bool HasEmbeddedSpaces(this string s) { foreach (var ch in s.Trim()) { if (ch == ' ') return true; } return false; } } }
StringLibrary 有三個靜態方法:
如果字串的開頭是大寫字元,則
StartsWithUpper
會傳回true
;否則會傳回false
。如果字串的開頭是小寫字元,則
StartsWithLower
會傳回true
;否則會傳回false
。如果字串包含內嵌空白字元,則
HasEmbeddedSpaces
會傳回true
;否則會傳回false
。
從最上層 Visual Studio 功能表中,依序選取 [建置]>[建置方案]。 組建應該會成功。
建立測試專案
下一步是建立單元測試專案,以測試 StringLibrary 程式庫。 執行下列步驟,以建立單元測試:
在 [方案總管] 中,以滑鼠右鍵按一下 UtilityLibraries 方案並選取 [新增]>[新增專案]。
在範本搜尋方塊中鍵入單元測試,選擇 [C#] 作為語言,然後選擇 [MSTest 單元測試專案] 作為 .NET 範本。 按一下 [下一步] 。
注意
在 Visual Studio 2019 版本 16.9 中,MSTest 專案範本名稱為單元測試專案。
將專案命名 StringLibraryTests ,然後按 [下一步]。
選擇建議的目標 Framework 或 .NET 8,然後選擇 [建立]。
注意
本入門教學課程會搭配使用 Live Unit Testing 與 MSTest 測試架構。 您也可以使用 xUnit 和 NUnit 測試架構。
單元測試專案無法自動存取所測試的類別程式庫。 您可以新增類別庫專案的參考,以提供測試程式庫存取權。 若要執行這個作業,請以滑鼠右鍵按一下
StringLibraryTests
專案,然後依序選取 [新增]>[專案參考]。 在 [參考管理員] 對話方塊中,確定已選取 [方案] 索引標籤,然後選取 StringLibrary 專案,如下圖所示。將範本所提供的未定案單元測試程式碼取代為下列程式碼:
using System; using Microsoft.VisualStudio.TestTools.UnitTesting; 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, $"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 ? "<null>" : word)}': " + $"false; Actual: {result}"); } } } }
選取工具列上的儲存圖示,以儲存專案。
因為單元測試程式碼包含一些非 ASCII 字元,您會看到下列對話方塊警告,如果您將檔案儲存為其預設 ASCII 格式,則會遺失部分字元。
選擇 [用其他編碼儲存] 按鈕。
在 [進階儲存選項] 對話方塊的 [編碼] 下拉式清單中,選擇 [Unicode (UTF-8 沒有簽章) - 字碼頁 65001],如下圖所示:
從最上層的 Visual Studio 功能表,選取 [建置]> [重建方案],以編譯單元測試專案。
您已為其建立類別庫以及一些單元測試。 您現在已完成使用 Live Unit Testing 所需的準備工作。
啟用 Live Unit Testing
到目前為止,雖然您已撰寫 StringLibrary 類別庫的測試,但尚未執行它們。 Live Unit Testing 會在啟用之後自動執行它們。 若要這麼做,請執行下列作業:
選擇性地選取包含 StringLibrary. 程式碼的程式碼編輯器視窗。 這是 Class1.cs (適用於 C# 專案) 或 Class1.vb (適用於 Visual Basic 專案) (此步驟可讓您在啟用 Live Unit Testing 之後,以視覺化方式檢查測試結果以及程式碼涵蓋範圍的範圍)。
從最上層 Visual Studio 功能表中,依序選取 [測試]>[Live Unit Testing]>[啟動]。
確保存放庫根目錄包含公用程式專案和測試專案的來源檔案路徑,以確認 Live Unit Testing 的組態。 選取 [下一步],然後選取 [完成]。
在 Live Unit Testing 視窗中,選取 [包含所有測試] 連結 (或者,選取 [播放清單] 按鈕圖示,然後選取 [StringLibraryTest],這會選取其下方的所有測試。然後取消選取 [播放清單] 按鈕以離開編輯模式。)
Visual Studio 會重建專案並啟動 Live Unit Test,以自動執行所有測試。
- Visual Studio 會重建專案並啟動 Live Unit Test,以自動執行所有測試。
完成測試執行時,Live Unit Testing 會顯示整體結果和個別測試結果。 此外,程式碼編輯器視窗會以圖形方式顯示測試程式碼涵蓋範圍和測試結果。 如下圖所示,已成功執行所有這三項測試。 它也會顯示我們的測試已涵蓋 StartsWithUpper
方法中的所有程式碼路徑,而且已成功執行這些測試 (以綠色核取記號 "✓" 指出)。 最後,它會顯示 StringLibrary 中沒有其他方法具有程式碼涵蓋範圍 (以藍線 "➖" 指出)。
您也可選取程式碼編輯器視窗中的特定程式碼涵蓋範圍圖示,以取得測試涵蓋範圍和測試結果的更詳細資訊。 若要檢查此詳細資料,請執行下列作業:
按一下
StartsWithUpper
方法之if (String.IsNullOrWhiteSpace(s))
行中的綠色核取記號。 如下圖所示,Live Unit Testing 指出三項測試涵蓋該程式碼行,而且全部已成功執行。按一下
StartsWithUpper
方法之return Char.IsUpper(s[0])
行中的綠色核取記號。 如下圖所示,Live Unit Testing 指出只有兩項測試涵蓋該程式碼行,而且全部已成功執行。
Live Unit Testing 所識別的主要問題是不完整的程式碼涵蓋範圍。 您將在下節中解決它。
展開測試涵蓋範圍
在本節中,您會將單元測試擴充至 StartsWithLower
方法。 當您這麼做時,Live Unit Testing 會動態繼續測試您的程式碼。
若要將程式碼涵蓋範圍擴充至 StartsWithLower
方法,請執行下列作業:
將下列
TestStartsWithLower
和TestDoesNotStartWithLower
方法新增至專案的測試原始程式檔:// Code to add to UnitTest1.cs [TestMethod] public void TestStartsWithLower() { // Tests that we expect to return true. string[] words = { "alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство" }; foreach (var word in words) { bool result = word.StartsWithLower(); Assert.IsTrue(result, $"Expected for '{word}': true; Actual: {result}"); } } [TestMethod] public void TestDoesNotStartWithLower() { // Tests that we expect to return false. string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα", "Москва", "1234", ".", ";", " "}; foreach (var word in words) { bool result = word.StartsWithLower(); Assert.IsFalse(result, $"Expected for '{word}': false; Actual: {result}"); } }
在
Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse
方法呼叫之後立即新增下列程式碼,來修改DirectCallWithNullOrEmpty
方法。// Code to add to UnitTest1.cs result = StringLibrary.StartsWithLower(word); Assert.IsFalse(result, $"Expected for '{(word == null ? "<null>" : word)}': " + $"false; Actual: {result}");
Live Unit Testing 會在您修改原始程式碼時自動執行新的和修改過的測試。 如下圖所示,所有測試 (包含您已新增的兩項測試以及已修改的一項測試) 都已成功。
切換至包含 StringLibrary 類別原始程式碼的視窗。 Live Unit Testing 現在會顯示我們的程式碼涵蓋範圍擴充至
StartsWithLower
方法。
在某些情況下,測試總管中的成功測試可能會呈灰色。這表示測試目前正在執行中,或者測試沒有再次執行,因為自上次執行以來沒有發生任何會影響測試的程式碼變更。
截至目前為止,所有測試均為成功。 在下節中,我們將檢查如何處理測試失敗。
處理測試失敗
在本節中,您將探索如何使用 Live Unit Testing 來識別、疑難排解和解決測試失敗。 做法是將測試涵蓋範圍展開至 HasEmbeddedSpaces
方法。
將下列方法新增至測試檔案:
[TestMethod] public void TestHasEmbeddedSpaces() { // Tests that we expect to return true. string[] phrases = { "one car", "Name\u0009Description", "Line1\nLine2", "Line3\u000ALine4", "Line5\u000BLine6", "Line7\u000CLine8", "Line0009\u000DLine10", "word1\u00A0word2" }; foreach (var phrase in phrases) { bool result = phrase.HasEmbeddedSpaces(); Assert.IsTrue(result, $"Expected for '{phrase}': true; Actual: {result}"); } }
測試執行時,Live Unit Testing 指出
TestHasEmbeddedSpaces
方法失敗,如下圖所示:選取顯示程式庫程式碼的視窗。 Live Unit Testing 已將程式碼涵蓋範圍擴充至
HasEmbeddedSpaces
方法。 它也會在失敗測試所涵蓋的程式行中新增紅色 "🞩",以報告測試失敗。將游標停留在含
HasEmbeddedSpaces
方法簽章的行上方。 Live Unit Testing 會顯示一個工具提示,報告某項測試涵蓋該方法,如下圖所示:選取失敗的 TestHasEmbeddedSpaces 測試。 Live Unit Testing 提供一些選項,例如執行所有測試和偵錯所有測試,如下圖所示:
選取 [全部偵錯] 以偵錯失敗的測試。
Visual Studio 會以偵錯模式執行測試。
測試會將陣列中的每個字串都指派給名為
phrase
的變數,並將它傳遞給HasEmbeddedSpaces
方法。 assert 運算式第一次為false
時,執行程式會暫停並叫用偵錯工具。 下圖顯示Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue
方法呼叫中的未預期值所造成的例外狀況對話方塊。此外,Visual Studio 所提供的所有偵錯工具還可以幫助我們針對失敗的測試進行疑難排解,如下圖所示:
請注意,在
phrase
變數值為 "Name\tDescription" (即陣列的第二個項目) 的 [自動變數] 視窗中。 測試方法預期HasEmbeddedSpaces
在收到此字串時傳回true
;但卻傳回false
。 顯然,它無法將 "\t" (定位字元) 辨識為內嵌空格。選取 [偵錯]>[繼續]、按 F5,或按一下工具列上的 [繼續] 按鈕,繼續執行測試程式。 因為發生無法處理的例外狀況,所以測試終止。 這提供初步調查 Bug 的足夠資訊。
TestHasEmbeddedSpaces
(測試常式) 的假設錯誤,或HasEmbeddedSpaces
未正確地辨識所有內嵌空格。若要診斷和修正問題,請使用
StringLibrary.HasEmbeddedSpaces
方法開始。 查看HasEmbeddedSpaces
方法中的比較。 它會將內嵌空格視為 U+0020。 不過,Unicode Standard 包含許多其他空格字元。 這可能表示不正確地測試程式庫程式碼的空格字元。將相等比較取代為 System.Char.IsWhiteSpace 方法呼叫:
if (Char.IsWhiteSpace(ch))
Live Unit Testing 會自動重新執行失敗的測試方法。
Live Unit Testing 會顯示更新的結果,也會出現在程式碼編輯器視窗中。