單元測試不是軟件開發的新概念,在1970年就一直存在,屢屢被證明是最理想的方法之一。
本系列將分成3節:
- 單元測試基礎知識
- 打破依賴,使用模擬對象,樁對象,測試框架
- 創建優秀的單元測試
本節索引:
單元測試幾乎總是基於框架來寫的,因為框架可以為我們提供統一的API來管理測試。
常用的框架有Unit Test(MS Test),NUnit(開源)
定義
單元測試是一段代碼調用另一段代碼,隨后檢驗一些假設的正確性。(單元指的是一個方法或函數)
集成測試是指把2個或多個互相依賴的軟件模塊作為一組進行測試。
優秀的單元測試准則
- 自動的,可重復
- 容易實現
- 持續可用
- 簡單
- 快速
對於TDD確切的含義,有很多不同的觀點,有人覺得就是測試優先的開發,有人覺得意味着大量的測試,有人覺得是一種設計方法。
TDD的流程:
寫測試 寫代碼 重構 寫下一個測試
它顯示了TDD是增量性質的,每次一小步,最終完成高質量的軟件。(重構可以在完成每個測試后進行,也可以在完成幾個測試后進行。重構是非常有價值意義的。)
TDD的優點:
- 較高的代碼測試覆蓋率
- 測試是可信賴的
- 輔助設計,減少代碼復雜度
所有的測試框架都共享相同的核心特性:Test Declaration, Test Execution, and Assertions.
在.Net中一般使用特性標簽來添加額外的信息,下面就是MS Test和NUnit在特性標簽上不同的地方。
MS Test Attribute | NUnit Attribute | 用途 |
[TestClass] | [TestFixture] | 定義一個測試類,里面可以包含很多測試函數和初始化、銷毀函數(以下所有標簽和其他斷言)。 |
[TestMethod] | [Test] | 定義一個獨立的測試函數。 |
[ClassInitialize] | [TestFixtureSetUp] | 定義一個測試類初始化函數,每當運行測試類中的一個或多個測試函數時,這個函數將會在測試函數被調用前被調用一次(在第一個測試函數運行前會被調用)。 |
[ClassCleanup] | [TestFixtureTearDown] | 定義一個測試類銷毀函數,每當測試類中的選中的測試函數全部運行結束后運行(在最后一個測試函數運行結束后運行)。 |
[TestInitialize] | [SetUp] | 定義測試函數初始化函數,每個測試函數運行前都會被調用一次。 |
[TestCleanup] | [TearDown] | 定義測試函數銷毀函數,每個測試函數執行完后都會被調用一次。 |
[AssemblyInitialize] | -- | 定義測試Assembly初始化函數,每當這個Assembly中的有測試函數被運行前,會被調用一次(在Assembly中第一個測試函數運行前會被調用)。 |
[AssemblyCleanup] | -- | 定義測試Assembly銷毀函數,當Assembly中所有測試函數運行結束后,運行一次。(在Assembly中所有測試函數運行結束后被調用) |
[DescriptionAttribute] | [Category] | 定義標識分組。 |
安裝
對於MS Test,只要安裝VS則會自動安裝。在工具欄==測試==窗口==測試資源管理器打開。
對於NUnit,點擊鏈接,下載安裝即可。
編碼
- 配置對象
- 操作對象
- 斷言結果
[TestClass] public class BlogTests { public DbContext Db { get; set; } /// <summary> /// 每個測試方法執行前都會執行 /// </summary> [TestInitialize] public void Init() { //1 配置對象 Db = new DbContext(); } [TestMethod] public void TestAdd() { var blog = new Blog { Title = "單元測試的藝術", Content = "單元測試是一門藝術" }; //2 操作對象 Db.Add(blog); //3 斷言結果 Assert.IsTrue(blog.Id > 0); } /// <summary> /// 每個測試方法執行后都會執行 /// </summary> [TestCleanup] public void Clean() { Db = null; } }
異常的測試
有時候,測試里面上需要拋出異常,這是業務上的正確性。在單元測試里,也有對應特性用來實現。如
[ExpectedException(typeof(OutOfMemoryException), AllowDerivedTypes = true)]//默認異常的子類也會不通過測試的 [TestMethod] public void TestAdd() { var blog = new Blog { Title = "單元測試的藝術", Content = "單元測試是一門藝術" }; //2 操作對象 Db.Add(blog); throw new OutOfMemoryException(); //3 斷言結果 Assert.IsTrue(blog.Id > 0); }
忽略的測試
有時候,測試寫的有問題,代碼沒問題。我們可以暫時忽略該測試
[Ignore] [TestMethod] public void TestAdd() { var blog = new Blog { Title = "單元測試的藝術", Content = "單元測試是一門藝術" }; //2 操作對象 Db.Add(blog); throw new OutOfMemoryException(); //3 斷言結果 Assert.IsTrue(blog.Id > 0); }
對測試分組
當我們只想測試某一類測試的時候,也有對應的特性
[TestCategory("change db")] [TestMethod] public void TestAdd() { var blog = new Blog { Title = "單元測試的藝術", Content = "單元測試是一門藝術" }; //2 操作對象 Db.Add(blog); //3 斷言結果 Assert.IsTrue(blog.Id > 0); } [TestCategory("no change")] [TestMethod] public void TestRead() { //2 操作對象 var blogs = Db.GetBlogs(); //3 斷言結果 Assert.IsTrue(blogs.Length > 0); }
運行選定的測試即可
測試
SUT Kind | SUT |
項目 | 新建一個【被測項目】.Tests的測試項目 |
類 | 至少為每個被測試類新建一個【被測類名】Tests的類 |
方法 | 至少為每個方法名新建一個【方法名】【測試場景】【預期行為】的方法 或者使用Test【方法名】的簡單命名 |
備注:SUT("system under test")代表被測系統,有些人喜歡CUT("code under test")。通常SUT。
本文作者:Never、C
本文鏈接:http://www.cnblogs.com/neverc/p/4742654.html