首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

使用 NUnit 進行單元測試實戰指南

     編輯:關於.NET

你是否熟悉這些痛點場景?

  1. 編譯-調試無限循環:功能模組寫完編譯通過,點按鈕卻發現資料顯示不出、存不進資料庫,只能設斷點層層跟蹤,精疲力盡。
  2. Bug 像打地鼠:改這裡壞那裡,幾十上百個類、成百上千個方法,根本顧不過來,永遠不知道還藏著多少 Bug。
  3. 現場演示翻車:答辯或客戶驗收時關鍵功能失效,事後發現只是底層工具類一個方法回傳 null 導致。

若上述場景令你痛心疾首,請繼續閱讀。

什麼是單元測試?

單元測試 的一個測試案例是一小段程式碼,用於驗證一個細小功能(通常是單一方法或幾個相關方法)行為是否正確。以下是實際專案中的測試案例:

/// 
/// 測試基本的新增與刪除角色功能
/// 
[Test]
public void TestAddAndRemoveRole()
{
    IRoleServices roleServ = UnityHelper.CreateContainer().Resolve<IRoleServices>();
    IRoleRepository roleRep = UnityHelper.CreateContainer().Resolve<IRoleRepository>();
    Assert.IsNotNull(roleServ);
    Assert.IsNotNull(roleRep);

    String timeStamp = DateTime.Now.ToString();
    RoleDto newRole = new RoleDto()
    {
        Name = "測試角色" + timeStamp,
        Desciption = "此角色僅供測試使用",
    };
    roleServ.AddRole(newRole);

    RoleDto addedRole = roleRep.GetRoleByName("測試角色" + timeStamp);
    Assert.AreNotEqual(-1, addedRole.ID); // 確認新增成功

    roleServ.RemoveRole(addedRole.ID);
    Assert.AreEqual(-1, roleRep.GetRoleByName("測試角色" + timeStamp).ID); // 確認刪除成功
}

此測試驗證 Services 層對角色的新增/刪除是否正常運作。

為何開發人員不寫單元測試?四大誤區解析

誤區 1:單元測試屬於測試工作,應由測試人員負責

正解: 單元測試雖叫「測試」,實屬開發範疇,應由開發人員編寫。開發人員至少應保證自己寫的每個方法行為正確,這是開發完成的基本門檻。

誤區 2:單元測試只是檢測代碼的工具

正解: 它是一種品質保證工具,更是給團隊成員與後續重構提供的「安全網」。交付類別時附上單元測試,對方隨時可跑測試驗證功能是否完好,重構時更是救命稻草。

誤區 3:專案經理沒要求,所以不用寫

正解: 寫單元測試應成為開發人員的本能,如同使用 IDE 寫代碼一樣自然,無需外部強制。

誤區 4:寫測試耗時,受益的是測試人員而非開發者

正解: 受益最大的正是開發人員自己:隨時獲得可信任的元件、重構無恐懼、Debug 定位極快、長遠收益遠超短期投入。

為何難以體會單元測試收益?

  1. 未親身嘗試:不嚐不知海參鮑魚滋味,跨出第一步最關鍵。
  2. 人性貪圖眼前利:省下 1 小時寫測試,日後卻花 3 小時抓 Bug,「貪小便宜吃大虧」。

.NET 平台實戰:NUnit 入門與專案實踐

NUnit 是開源、免費、輕量且強大的 .NET 單元測試框架(官網:nunit.org)。本文以國家 863 專案為例,該專案採敏捷開發、測試驅動、持續整合,要求:

  • 每個新類別必須有配套單元測試
  • Commit SVN 前必須跑通所有測試
  • 配合介面導向程式設計與 Mock 技術提高可測試性

專案結構

解決方案結構圖

圖1:XUnit 專案為單元測試專案

實戰案例:UnityHelper 單元測試

專案使用 Unity 依賴注入,封裝 UnityHelper 取得容器單例:

public class UnityHelper
{
    private static IUnityContainer _container = null;
    public static IUnityContainer CreateContainer()
    {
        if (null == _container)
        {
            _container = new UnityContainer();
            // 從 unity.cfg.xml 讀取 IoC 配置
            ExeConfigurationFileMap map = new ExeConfigurationFileMap();
            map.ExeConfigFilename = "unity.cfg.xml";
            Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
            UnityConfigurationSection section = config.GetSection("unity") as UnityConfigurationSection;
            section.Containers["defaultContainer"].Configure(_container);
        }
        return _container;
    }
}

測試四大核心點:

  1. 能正確回傳 UnityContainer
  2. Container 能正確建立物件
  3. Container 本身為單例
  4. 配置為單例的物件確實為單例

測試代碼

[TestFixture]
public class UnityHelperTests
{
    [Test]
    public void TestCreateUnityContainer()
    {
        Assert.IsNotNull(UnityHelper.CreateContainer());
        Assert.IsInstanceOf(typeof(IUnityContainer), UnityHelper.CreateContainer());
    }

    [Test]
    public void TestCreateObject()
    {
        IRoleRepository roleRepository = UnityHelper.CreateContainer().Resolve<IRoleRepository>();
        Assert.IsNotNull(roleRepository);
        Assert.IsInstanceOf(typeof(NHRoleRepository), roleRepository);

        IRoleServices roleServ = UnityHelper.CreateContainer().Resolve<IRoleServices>();
        Assert.IsNotNull(roleServ);
        Assert.IsInstanceOf(typeof(RoleServicesImpl), roleServ);
    }

    [Test]
    public void TestSingletonContainer()
    {
        Assert.AreSame(UnityHelper.CreateContainer(), UnityHelper.CreateContainer());
    }

    [Test]
    public void TestSingletonObject()
    {
        Assert.AreSame(
            UnityHelper.CreateContainer().Resolve<IRoleRepository>(),
            UnityHelper.CreateContainer().Resolve<IRoleRepository>()
        );
    }
}

NUnit GUI 執行流程

  1. 專案參考 nunit.framework.dll,引入 NUnit.Framework 命名空間
  2. 測試類別標記 [TestFixture],測試方法標記 [Test]
  3. 編譯測試專案產生 DLL
  4. 開啟 NUnit GUI,File → Open Project 載入測試 DLL
  5. 選中測試套件點擊 Run
NUnit GUI介面 載入測試專案 測試通過綠燈

圖2-4:NUnit GUI 操作流程與通過結果

測試失敗時的快速定位

故意破壞單例邏輯後重跑,紅燈精準指出失敗點:

測試失敗紅燈

圖5:容器單例性被破壞,測試精準報錯

總結

單元測試不是枯燥工作,每次看到綠燈通過都是最爽時刻。請從下一個專案、下一個類別、甚至下一個方法,開始你的單元測試之旅。推薦參考《Pragmatic Unit Testing in C# with NUnit》一書深入學習。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved