對市集應用程式中的 Visual C# 程式碼進行單元測試
本主題說明使用 Visual Studio Express 2012 for Windows 8 和 Microsoft Unit Testing Framework,建立 Windows 市集應用程式所使用 Visual C# 類別之單元測試的方式。 Rooter 類別會藉由實作計算某數值的平方根估計數的函式,示範微積分中極限理論的模糊記憶。 然後 Maths 應用程式就可以使用這個函式向使用者展現許多可運用數學運算執行的有趣作業。
本主題示範如何使用單元測試做為開發工作的第一步。 採用這種方式時,您會先撰寫測試方法,用來驗證要測試之系統中的特定行為,然後撰寫通過測試的程式碼。 依照下列程序的順序進行變更,您就可以反轉策略,先撰寫要測試的程式碼,再撰寫單元測試。
本主題還會建立單一 Visual Studio 方案,以及用於單元測試和要測試之 DLL 的個別專案。 您也可以直接在 DLL 專案中包含單元測試,或是針對單元測試和 DLL 建立個別方案。
注意事項 |
---|
Visual Studio Ultimate、VS Premium 和 VS Professional 都有為單元測試提供額外的功能。
如需詳細資訊,請參閱 MSDN Library 中的 Verifying Code by Using Unit Tests。 |
本主題內容
建立方案和單元測試專案
驗證測試總管中執行的測試
將 Rooter 類別新增至 Maths 專案
將測試專案與應用程式專案結合
反覆擴大測試範圍並讓測試成功
對失敗的測試進行偵錯
重構程式碼
建立方案和單元測試專案
選擇 [檔案] 功能表上的 [新增],然後選擇 [新專案]。
在 [新增專案] 對話方塊上,展開 [已安裝的],然後展開 [Visual C#],並選擇 [Windows 市集]。 然後從專案範本清單中選擇 [空白應用程式]。
將專案命名為 Maths,並確認已選取 [為方案建立目錄]。
在 [方案總管] 中選擇方案名稱,從捷徑功能表選擇 [新增],然後選擇 [新專案]。
在 [新增專案] 對話方塊上,展開 [已安裝的],然後展開 [Visual C#],並選擇 [Windows 市集]。 接著從專案範本清單中選擇 [單元測試程式庫 (Windows 市集應用程式)]。
在 Visual Studio 編輯器中開啟 UnitTest1.cs。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; using Maths; namespace RooterTests { [TestClass] public class UnitTest1 [TestMethod] public void TestMethod1() { }
請注意:
每一項測試都是使用 [TestMethod] 定義。 測試方法必須傳回 void,而且不可以有任何參數。
測試方法必須在以 [TestClass] 屬性裝飾的類別中。
在測試執行時,會建立每個測試類別的執行個體。 測試方法會依照非特定順序呼叫。
您可以定義在每個模組、類別或方法之前和之後叫用的特殊方法。 如需詳細資訊,請參閱 MSDN Library 中的在單元測試中使用 Microsoft.VisualStudio.TestTools.UnitTesting 成員。
驗證測試總管中執行的測試
在 UnitTest1.cs 檔案的 TestMethod1 中插入一些測試程式碼:
[TestMethod] public void TestMethod1() { Assert.AreEqual(0, 0); }
請注意,Assert 類別會提供數種靜態方法,可讓您用來驗證測試方法的結果。
選擇 [測試] 功能表上的 [執行],然後選擇 [全部執行]。
測試專案隨即建置並執行。 [測試總管] 視窗隨即開啟,而且測試會在 [通過的測試] 底下列出。 視窗底部的 [摘要] 窗格會提供有關所選取測試的其他詳細資料。
將 Rooter 類別新增至 Maths 專案
在 [方案總管] 中選擇 [Maths] 專案名稱。 從捷徑功能表中選擇 [新增],然後選擇 [類別]。
將類別檔案命名為 Rooter.cs
將下列程式碼新增至 Rooter 類別 Rooter.cs 檔案:
public Rooter() { } // estimate the square root of a number public double SquareRoot(double x) { return 0.0; }
Rooter 類別會宣告建構函式和 SqareRoot 評估工具方法。
SqareRoot 方法只是最簡單的實作,剛好足夠進行測試設定的基本結構測試。
將測試專案與應用程式專案結合
將 Maths 應用程式的參考新增至 RooterTests 專案。
在 [方案總管] 中選擇 [RooterTests] 專案,然後在捷徑功能表上選擇 [加入參考]。
在 [加入參考 - RooterTests] 對話方塊中,展開 [方案] 並選擇 [專案]。 然後選取 [Maths] 項目。
將 using 陳述式新增至 UnitTest1.cs 檔案:
開啟 UnitTest1.cs。
將這個程式碼新增至 using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; 這一行下方:
using Maths;
新增使用 Rooter 函式的測試。 將下列程式碼新增至 UnitTest1.cpp:
[TestMethod] public void BasicTest() { Maths.Rooter rooter = new Rooter(); double expected = 0.0; double actual = rooter.SquareRoot(expected * expected); double tolerance = .001; Assert.AreEqual(expected, actual, tolerance); }
建置方案。
新測試會出現在 [測試總管] 的 [未執行的測試] 節點中。
在 [測試總管] 中選擇 [全部執行]。
您已設定測試和程式碼專案,並且確認可以執行在程式碼專案中執行函式的測試。 現在您可以開始撰寫實際的測試和程式碼。
反覆擴大測試範圍並讓測試成功
加入新的測試:
[TestMethod] public void RangeTest() { Rooter rooter = new Rooter(); for (double v = 1e-6; v < 1e6; v = v * 3.2) { double expected = v; double actual = rooter.SquareRoot(v*v); double tolerance = ToleranceHelper(expected); Assert.AreEqual(expected, actual, tolerance); } }
提示
建議您不要變更成功的測試。而是加入新的測試,更新程式碼讓測試成功,然後新增另一個測試,依此類推。
當使用者的需求變更時,停用不再正確的測試。撰寫新測試,並以相同的遞增方式一次讓一項測試成功。
在 [測試總管] 中選擇 [全部執行]。
測試失敗。
提示
在您撰寫每一項測試之後立即確認其失敗。這樣有助於避免犯下撰寫永遠不會失敗的測試這種簡單的錯誤。
透過測試強化程式碼,讓新的測試都成功。 將 Rooter.cs 中的 SqareRoot 函式變更如下:
public double SquareRoot(double x) { double estimate = x; double diff = x; while (diff > estimate / 1000) { double previousEstimate = estimate; estimate = estimate - (estimate * estimate - x) / (2 * estimate); diff = Math.Abs(previousEstimate - estimate); } return estimate; }
建置方案,然後在 [測試總管] 中選擇 [全部執行]。
現在三項測試都會成功。
提示
藉由一次新增一項測試的方式開發程式碼。確定在每次反覆運算後,所有測試都成功。
對失敗的測試進行偵錯
新增另一項測試至 UnitTest1.cs:
// Verify that negative inputs throw an exception. [TestMethod] public void NegativeRangeTest() { string message; Rooter rooter = new Rooter(); for (double v = -0.1; v > -3.0; v = v - 0.5) { try { // Should raise an exception: double actual = rooter.SquareRoot(v); message = String.Format("No exception for input {0}", v); Assert.Fail(message); } catch (ArgumentOutOfRangeException ex) { continue; // Correct exception. } catch (Exception e) { message = String.Format("Incorrect exception for {0}", v); Assert.Fail(message); } } }
在 [測試總管] 中選擇 [全部執行]。
測試失敗。 在 [測試總管] 中選擇測試名稱。 失敗的判斷提示會反白顯示。 失敗的訊息會在 [測試總管] 的詳細資料窗格中顯示。
若要查看測試失敗的原因,請逐步執行函式:
在 SquareRoot 函式的開頭設定中斷點。
在失敗的測試之捷徑功能表上,選擇 [偵測選取的測試]。
執行到中斷點停止時,請逐步執行程式碼。
將程式碼新增至 Rooter 方法以擷取例外狀況:
public double SquareRoot(double x) { if (x < 0.0) { throw new ArgumentOutOfRangeException(); }
- 在 [測試總管] 中,選擇 [全部執行] 測試修正過的方法,並確定並未導入回復 (Regression)。
現在所有測試都會成功。
重構程式碼
簡化 SquareRoot 函式的主要計算。
變更結果實作
// old code //result = result - (result*result - v)/(2*result); // new code result = (result + v/result) / 2.0;
選擇 [全部執行] 測試重構的方法,並確定並未導入回復 (Regression)。
提示
一組穩定而良好的單元測試,可確認您並未在變更程式碼時引入錯誤。
重構測試程式碼以消除重複的程式碼。
請注意,RangeTest 方法會對 Assert 方法中所使用容錯變數的分母採用硬式編碼。 如果您打算新增其他使用相同容錯計算的測試,則在多個位置使用硬式編碼值可能導致錯誤。
將私用方法新增至 Unit1Test 類別以計算容錯值,然後改為呼叫該方法。
private double ToleranceHelper(double expected) { return expected / 1000; } ... [TestMethod] public void RangeTest() { ... // old code // double tolerance = expected/1000; // new code double tolerance = ToleranceHelper(expected); Assert.AreEqual(expected, actual, tolerance); } ...
選擇 [全部執行] 測試重構的方法,並確定並未導入錯誤。
注意事項 |
---|
若要將協助程式方法新增至測試類別,請不要將 [TestMethod] 屬性新增至方法。[測試總管] 並未登錄要執行的方法。 |