在軟件開發過程中,我們可能會有很多的模塊,而每個模塊有可能又由許多函數組成。當我們的系統發生錯誤時,我們必須定位發生錯誤的模塊,然後精確到模塊中某個具體的函數中,而這工作往往又是非常浪費時間和生產效率的,如果系統越複雜,那麼定位錯誤的成本將越高。所以在每個函數集成進模塊時,必須通過嚴格的單元測試來驗證。
在VS2010中我們可以爲我們的函數自動生成單元測試,無論它是否是public或者的private的。所有用於單元測試的類和函數都被定義在Microsoft.VisualStudio.TestTools.UnitTesting這個命名空間中。
創建Unit Test
我們先創建一個被測試的類庫工程,名字叫TestLibrary,然後添加如下代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestLibrary { public class Class1 { public double CalculateTotalPrice(double quantity) { double totalPrice; double unitPrice; unitPrice = 16.0; totalPrice = unitPrice * quantity; return totalPrice; } public void GetTotalPrice() { int qty = 5; double totalPrice = CalculateTotalPrice(qty); Console.WriteLine("總價: " + totalPrice); } } }
然後我們在需要單元測試的函數上鼠標右鍵,如圖會有個Create Unit Tests選項。
點擊該選項後,就會彈出如下窗口,該窗口會顯示出該工程和類中所有的函數,這裏我們可以選擇我們要進行單元測試的函數。
我們選擇CalculateTotalPrice和GetTotalPrice兩個函數,點擊OK,然後輸入測試工程的名字點Create。(我們可以在Output project選項中選擇一個以創建的工程或者創建一個新的測試工程)我們的單元測試代碼就自動創建好了,當然,這個自動創建的測試代碼並沒有完成的,而是爲我們的單元測試搭好了框架而已。自動生成的代碼如下:
using TestLibrary; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; namespace TestProject1 { ///<summary> ///This is a test class for Class1Test and is intended ///to contain all Class1Test Unit Tests ///</summary> [TestClass()] public class Class1Test { private TestContext testContextInstance; ///<summary> ///Gets or sets the test context which provides ///information about and functionality for the current test run. ///</summary> public TestContext TestContext { get { return testContextInstance; } set { testContextInstance = value; } } #region Additional test attributes // //You can use the following additional attributes as you write your tests: // //Use ClassInitialize to run code before running the first test in the class //[ClassInitialize()] //public static void MyClassInitialize(TestContext testContext) //{ //} // //Use ClassCleanup to run code after all tests in a class have run //[ClassCleanup()] //public static void MyClassCleanup() //{ //} // //Use TestInitialize to run code before running each test //[TestInitialize()] //public void MyTestInitialize() //{ //} // //Use TestCleanup to run code after each test has run //[TestCleanup()] //public void MyTestCleanup() //{ //} // #endregion ///<summary> ///A test for CalculateTotalPrice ///</summary> [TestMethod()] public void CalculateTotalPriceTest() { Class1 target = new Class1(); // TODO: Initialize to an appropriate value double quantity = 0F; // TODO: Initialize to an appropriate value double expected = 0F; // TODO: Initialize to an appropriate value double actual; actual = target.CalculateTotalPrice(quantity); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); } ///<summary> ///A test for GetTotalPrice ///</summary> [TestMethod()] public void GetTotalPriceTest() { Class1 target = new Class1(); // TODO: Initialize to an appropriate value target.GetTotalPrice(); Assert.Inconclusive("A method that does not return a value cannot be verified."); } } }
其實,我們可以在創建單元測試時適當控制自動生成的測試代碼,如圖我們點擊Setting按鈕。
這時會彈出如下圖的窗口
在該對話框中,我們可以對生成的測試文件、測試類以及測試方法自定義名稱。
-
Mark all test results Inconclusive by default:選中該複選框可爲每個測試方法提供 Assert.Inconclusive() 語句作爲佔位符 Assert。清除該複選框可消除佔位符 Assert。
-
Enable generation warnings:在測試函數創建中如果遇到任何的錯誤,代碼生成器會將這些錯誤信息以註釋的形式寫在生成的代碼中。
-
Globally qualify all types:這個選項是用來解決多個類可能有相同名字的函數問題,單元測試文件可能包含有多個類的測試函數,所以有可能會有同名的衝突,所以爲了區分開同名的函數,會在測試命名中添加namespaces。
-
Enable documentation comments:選中此複選框可爲每個測試方法提供佔位符註釋。清除該複選框可消除佔位符註釋。
-
Honor InternalsVisibleTo Attribute:選中該複選框可使標爲 Friend 或 Internal 的方法被視爲公共方法(推薦)。清除該複選框則需要使用專用訪問器測試這些方法。
此外還可以注意到自動生成的代碼中有一些被註釋的方法,這些方法我們可以選擇使用:
- [ClassInitialize()]標記的方法可在運行類中的第一個測試前運行代碼。
- [ClassCleanUp()]標記的方法可在運行完類中的所有測試後運行代碼。
- [TestInitialize()]標記的方法可在運行每個測試前運行代碼。
- [TestCleanUp()]標記的方法可在運行完每個測試後運行代碼。
Assert語句
Assert語句用來比較從方法返回來的值和期望值,然後返回pass或者fail的結果。如果在一個測試方法中有多個Assert的話,那麼這個測試方法要通過測試必須讓所有的Assert方法通過,不然,其中有一個fail,那麼這個Case就會fail。如果我們的單元測試中沒有任何的Assert語句,那麼它的結果始終是pass的。
Assert類有許多進行比較的方法,此外還有StringAsserts、CollectionAssert類也可用於單元測試中,具體的我們可以參加MSDN上的介紹了。
下面我們修改一下CalculateTotalPriceTest()這段測試代碼,並將最後的Assert.Inconclusive註釋:
[TestMethod()] public void CalculateTotalPriceTest() { Class1 target = new Class1(); double quantity = 10F; double expected = 160F; double actual; actual = target.CalculateTotalPrice(quantity); Assert.AreEqual(expected, actual); //Assert.Inconclusive("Verify the correctness of this test method."); }
最後我們可以在該函數中,右鍵鼠標然後點擊Run Tests運行測試一下我們的代碼。我們就可以在Test Results窗口中看見我們運行的結果。