[Test] 單元測試藝術(1) 基礎知識


單元測試不是軟件開發的新概念,在1970年就一直存在,屢屢被證明是最理想的方法之一。

本系列將分成3節:

  1. 單元測試基礎知識
  2. 打破依賴,使用模擬對象,樁對象,測試框架
  3. 創建優秀的單元測試

 

本節索引:

 

單元測試與集成測試

單元測試幾乎總是基於框架來寫的,因為框架可以為我們提供統一的API來管理測試。

常用的框架有Unit Test(MS Test),NUnit(開源)

 

定義

單元測試是一段代碼調用另一段代碼,隨后檢驗一些假設的正確性。(單元指的是一個方法或函數)

集成測試是指把2個或多個互相依賴的軟件模塊作為一組進行測試。

 

優秀的單元測試准則

  1. 自動的,可重復
  2. 容易實現
  3. 持續可用
  4. 簡單
  5. 快速

 

 

測試驅動(TDD)開發

對於TDD確切的含義,有很多不同的觀點,有人覺得就是測試優先的開發,有人覺得意味着大量的測試,有人覺得是一種設計方法。

 

TDD的流程:

寫測試 寫代碼 重構 寫下一個測試

它顯示了TDD是增量性質的,每次一小步,最終完成高質量的軟件。(重構可以在完成每個測試后進行,也可以在完成幾個測試后進行。重構是非常有價值意義的。)

 

TDD的優點:

  1. 較高的代碼測試覆蓋率
  2. 測試是可信賴的
  3. 輔助設計,減少代碼復雜度

 

 

MS Test和NUnit

所有的測試框架都共享相同的核心特性: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,點擊鏈接,下載安裝即可。

編碼

  1. 配置對象
  2. 操作對象
  3. 斷言結果
[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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM