.Net單元測試xUnit和集成測試指南(1)

来源:https://www.cnblogs.com/ruipeng/p/18112221
-Advertisement-
Play Games

引言 在現代化的軟體開發中,單元測試和集成測試是確保代碼質量和可靠性的關鍵部分。ASP.NET Core 社區內提供了強大的單元測試框架,xUnit 是其中之一,它提供了簡單、清晰和強大的測試功能,編寫單元測試有許多優點;有助於回歸、提供文檔及輔助良好的設計。下麵幾節我們來深入淺出探討如何使用 xU ...


引言

在現代化的軟體開發中,單元測試集成測試是確保代碼質量和可靠性的關鍵部分。ASP.NET Core 社區內提供了強大的單元測試框架,xUnit 是其中之一,它提供了簡單、清晰和強大的測試功能,編寫單元測試有許多優點;有助於回歸、提供文檔及輔助良好的設計。下麵幾節我們來深入淺出探討如何使用 xUnit 進行 ASP.NET Core 應用程式的單元測試和集成測試。

內容大綱:

xUnit 簡介

xUnit.net 是一個免費、開源、面向社區的.NET 單元測試工具。由 NUnit v2 的原始發明者編寫,xUnit.net 是用於 C#F#(其他.NET 語言可能也可以使用,但不受支持)的最新技術單元測試。xUnit.net 可與 Visual StudioVisual Studio CodeReSharperCodeRushTestDriven.NET 一起使用。它是.NET 基金會的一部分,並遵守其行為準則。其許可協議為 Apache 2(為 OSI 批准的許可協議)。

xUnit.net 官方網站

創建單元測試項目

在單元測試中通常要遵循AAA模式,也就是 ArrangeActAssert,這是一種常見的測試組織結構。

  • Arrange(準備): 在這個階段,將設置測試的前提條件,初始化對象、設置輸入參數等。簡單講就是準備測試環境,確保被測代碼在正確的上下文中執行。
  • Act(執行): 在這個階段,會執行要測試的代碼或方法。這是針對被測代碼的實際調用或操作。
  • Assert: 在這個階段,會驗證被測代碼的行為是否符合預期。檢查實際結果與期望結果是否一致,如果不一致則測試失敗。

示例:

[Fact]
public void Add_EmptyString_ReturnsZero()
{
    // Arrange
    var stringCalculator = new StringCalculator();

    // Act
    var actual = stringCalculator.Add("");

    // Assert
    Assert.Equal(0, actual);
}

可讀性是編寫單元測試最重要的方面之一,在測試中分離這些操作 都明確地突出調用代碼所需的依賴項、調用代碼的方式以及嘗試斷言的內容,讓測試儘可能具有可讀性。

好了理解了這個核心概念我們可以先創建項目一步步的練習了。

用 VS 創建單元測試項目

image

在項目創建完之後我們可以簡單瀏覽一下 xUnit 單元測試項目裝了那些 nuget 依賴,做到對項目有個簡單的瞭解

  <ItemGroup>
    <PackageReference Include="coverlet.collector" Version="6.0.0" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
    <PackageReference Include="xunit" Version="2.5.3" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
  </ItemGroup>

下麵我們創建一個簡單的數據計算類。

  • 創建數學計算類
public class MathCalculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }
}
  • 創建數據計算測試類
public class MathCalculatorTests
{
    [Fact]
    public void Add_TwoNumbers_ReturnSum()
    {
        // Arrange
        var calculator = new MathCalculator();

        // Act
        var result = calculator.Add(3, 5);

        // Assert
        Assert.Equal(8, result);
    }
}

測試一下,測試類庫右鍵->運行測試

image

可以看到 我們的單元測試通過。

單元測試命名規範

本著代碼自文檔的原則,測試的名稱建議應包括三個部分:

  • 要測試的方法的名稱。
  • 測試的方案。
  • 調用方案時的預期行為。

示例

    [Fact]
    public void Add_TwoNumbers_ReturnSum()
    {
        // Arrange
        var calculator = new MathCalculator();

        // Act
        var result = calculator.Add(3, 5);

        // Assert
        Assert.Equal(8, result);
    }

要測試的方法名稱是 MathCalculator 中的 Add 方法,測試的方案是傳兩個數,預期是返回兩數之和 按照上面的測試名稱的命名規則可以命名為Add_TwoNumbers_ReturnSum

單元測試最佳命名規範應該包括三個關鍵部分:要測試的方法的名稱、測試的場景,以及調用該場景時的預期行為。良好的命名標準能清晰表達測試意圖,提供有效文檔,便於他人理解代碼行為和快速定位問題。

單元測試最佳實踐


將方法標記為測試方法在 xUnit 中有兩個屬性,FactTheory

Fact 屬性

在方法上我們看到有一個 Attribute [Fact] ,[Fact] 屬性是 xUnit 中最基本的測試屬性之一,用於標記一個方法作為一個無需參數且不返回任何內容的測試方法。被標記為 [Fact] 的方法將會被 xUnit 框架識別並執行.

Theory 屬性

Theory 屬性用於標記一個測試方法,該方法可以接受參數並運行多次,每次運行時使用不同的參數值。Theory 屬性通常用於數據驅動測試,允許在同一個測試方法中使用不同的輸入數據進行測試.

InlineData 屬性

[InlineData] 屬性指定這些輸入 Theory 標記的測試方法的參數值。

示例:

[Theory]
[InlineData(-1)]
[InlineData(0)]
[InlineData(1)]
public void IsPrime_ValuesLessThan2_ReturnFalse(int value)
{
    var result = _primeService.IsPrime(value);

    Assert.False(result, $"{value} should not be prime");
}

InlineData 適用於靜態、硬編碼的測試數據集合,適合於簡單且固定的測試場景。

MemberData 屬性

MemberData 屬性是 xUnit 中用於數據驅動測試的一種方式,它允許從一個欄位、屬性或方法中獲取測試數據,並將這些數據傳遞給測試方法進行多次測試。通過 MemberData 屬性,可以更靈活地管理和提供測試數據,適用於需要動態生成測試數據的情況。

使用方式

  • 標記測試方法:使用 [Theory] 屬性標記測試方法,以便接受從 MemberData 屬性提供的測試數據。
  • 準備測試數據:創建一個公共靜態欄位、屬性或方法,該欄位、屬性或方法返回一個 IEnumerable<object[]> 對象,其中每個 object[] 對象代表一組測試數據。
  • 傳遞測試數據:在 MemberData 屬性中指定要使用的數據源,從而將數據傳遞給測試方法。

示例

    public static IEnumerable<object[]> GetComplexTestData()
    {
        yield return new object[] { 10, 5, 15 }; // 測試數據 1
        yield return new object[] { -3, 7, 4 }; // 測試數據 2
        yield return new object[] { 0, 0, 0 }; // 測試數據 3
        // 可以根據需要繼續添加更多的測試數據
    }

   [Theory]
   [MemberData(nameof(GetComplexTestData))]
   public void Add_TwoNumbers_ReturnsSumofNumbers01(int first, int second, int sum)
   {
       // Arrange
       var calculator = new MathCalculator();

       // Act
       var result = calculator.Add(first, second);

       // Assert
       Assert.Equal(sum, result);
   }

MemberData 適用於動態、靈活的測試數據集合,適合於需要從外部源動態獲取測試數據的情況。

image

自定義屬性

除了上面提到的 InlineDataMemberData 之外還可以有更加靈活的方式繼承DataAttribute實現自定義的Attribute

我們來做一個實現和上面一樣的需求

  • 實現 Custom Attribute

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class CustomDataAttribute : DataAttribute
{

    private readonly int _first;
    private readonly int _second;
    private readonly int _sum;

    public CustomDataAttribute(int first, int second, int sum)
    {
        _first = first;
        _second = second;
        _sum = sum;
    }
    public override IEnumerable<object[]> GetData(MethodInfo testMethod)
    {
        yield return new object[] { _first, _second, _sum };
    }
}



  • 用例
   [Theory]
   [CustomData(1, 2, 3)]
   [CustomData(2, 3, 5)]
   public void Add_TwoNumbers_ReturnSum03(int num1, int num2, int expectedSum)
   {
       // Arrange
       var calculator = new MathCalculator();

       // Act
       var result = calculator.Add(num1, num2);

       // Assert
       Assert.Equal(expectedSum, result);
   }

自定義屬性相較於使用 InlineDataMemberData 有以下優勢:

  1. 靈活性:自定義屬性允許您實現更複雜的邏輯來動態生成測試數據,可以從不同數據源中獲取數據,實現更靈活的數據驅動測試。

  2. 重用性:通過自定義屬性,您可以將相同的測試數據邏輯應用於多個測試方法,提高測試代碼的重用性和可維護性。

  3. 可擴展性:自定義屬性可以根據需求進行定製和擴展,適應不同的測試場景和數據需求,使得測試數據的生成更具靈活性。

  4. 可讀性:通過自定義屬性,可以使測試代碼更具可讀性和表達力,更清晰地表達測試數據的來源和意圖。

儘管使用 InlineDataMemberData 可以滿足大多數簡單的測試數據需求,但當需要更複雜的數據生成邏輯、數據源、或者對測試數據進行處理時,使用自定義屬性會更具優勢,能夠更好地滿足個性化的測試需求。

在測試中應避免邏輯

[Theory]的出現就是為了避免我們在單元測試時編寫一些額外的邏輯,造成測試之外的一些錯誤。

編寫單元測試時,請避免手動字元串串聯、邏輯條件(例如 if、while、for 和 switch)以及其他條件。

錯誤示範:

   [Fact]
   public void Add_TwoNumbers_ReturnsSumofNumbers02()
   {
       // Arrange
       var calculator = new MathCalculator();
       var testData = new List<(int, int, int)>
   {
       (1, 2, 3),
       (2, 3, 5),
       (3, 4, 7)
   };

       // Act & Assert
       foreach (var (first, second, sum) in testData)
       {
           var result = calculator.Add(first, second);
           Assert.Equal(sum, result);
       }
   }

此處用了 forEach 迴圈來批量斷言,違反了單元測試的最佳實踐。

測試中應避免邏輯的好處是:

  • 降低在測試中引入 bug 的可能性。
  • 專註於最終結果,而不是實現細節。

ITestOutputHelper 控制台輸出

在 xUnit 中我們利用 Console.WriteLine輸出時發現什麼也不會顯示,在 xUnit 單元測試項目中我們需要利用ITestOutputHelper
ITestOutputHelper是 xUnit 中的一個介面,用於在單元測試中輸出信息。通過 ITestOutputHelper,您可以在測試運行時將調試信息、日誌信息等輸出到測試結果中,方便調試和查看測試過程中的輸出信息。

調試

再要測試的方法上右鍵選擇調試測試,或者點擊方法上面的小點
image

最後

本篇文章簡單的講了單元測試的基礎知識,讓大家先對單元測試有個基本的概念,這些用在具體的項目中顯然是不夠的,後面的章節我們聊一下 TDD,Fake 管理,Log 日誌輸出,單元測試覆蓋率,WebApi 的集成測試,DependencyInjection,Bogus,還有 Devops 的單元測試等知識。

本文完整源代碼

本文來自博客園,作者:董瑞鵬,轉載請註明原文鏈接:https://www.cnblogs.com/ruipeng/p/18112221


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 拓展閱讀 搜索引擎-01-概覽 搜索引擎-02-分詞與全文索引 搜索引擎-03-搜索引擎原理 Crawl htmlunit 模擬瀏覽器動態 js 爬蟲入門使用簡介 Crawl jsoup 爬蟲使用 jsoup 無法抓取動態 js 生成的內容 Crawl WebMagic 爬蟲入門使用簡介 webma ...
  • 枚舉類型 目錄枚舉類型1. 定義2. 枚舉元素的值2.1 預設2.2 全部賦值2.3 部分賦值3. 枚舉變數的定義方式3.1 先定義枚舉類型,再定義枚舉變數3.2 同時定義枚舉類型和枚舉變數3.3 忽略枚舉名,直接定義枚舉變數3.4 結合typedef關鍵字4. 總結 1. 定義 枚舉是用來代表整數 ...
  • Avalonia是一個強大的跨平臺UI框架,允許開發者構建豐富的桌面應用程式。 它提供了眾多UI組件、靈活的佈局系統、可定製的樣式以及事件處理機制。 在這篇博客中,我們將詳細解析Avalonia的UI組件、UI組件的生命周期、佈局、樣式和事件處理。 一、UI組件 Avalonia提供了豐富的UI組件 ...
  • 前言 樹形下拉菜單是許多WPF應用程式中常見的用戶界面元素,它能夠以分層的方式展示數據,提供更好的用戶體驗。本文將深入探討如何基於WPF創建一個可定製的樹形下拉菜單控制項,涵蓋從原理到實際實現的關鍵步驟。 一、需求分析 樹形下拉菜單控制項的核心是將ComboBox與TreeView結合起來,以實現下拉時 ...
  • 面向對象編程(OOP)是一種使用對象及其相互作用設計應用和電腦程式的編程範例。 OOP 中有一些基本的編程概念: 抽象化 (抽象化,也在我們編程世界中 所有類都是抽象化,物以類聚,擁有共同的特性或者行為) 椅子類 人類 動物類 【本質就是歸類】 多態性 【一類多種表現形態】【本質就是抽象化的程度】 ...
  • Avalonia中的Window 在Avalonia中,Window是一個基本的UI元素,它代表了一個應用程式的視窗。每個Window都可以包含其他的UI元素,如按鈕、文本框等,並可以響應各種用戶輸入事件。 在下麵的例子中,制定了當前應用的Window是MainWindow public parti ...
  • 目錄1.Redis簡介2.使用場景3.C# 具體使用介紹(Nuget)StackExchange.RedisFreeRedisNewLife.RedisServiceStack.Redis (收費)4.Redis 常用面試問題以及回答5.建議及經驗分享建議Redis 經驗分享ShareFlow 1. ...
  • 上一篇介紹了 IL 指令的分類以及參數載入指令,該載入指令以ld開頭,將參數載入到棧中,以便於後續執行操作命令。本篇開始介紹參數存儲指令,其指令以st開頭,將棧中的數據,存儲到指定的變數中,以方便後續使用。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...