Agent Skills: AwesomeAssertions 流暢斷言指南

|

UncategorizedID: kevintsengtw/dotnet-testing-agent-skills/dotnet-testing-awesome-assertions-guide

Install this agent skill to your local

pnpm dlx add-skill https://github.com/kevintsengtw/dotnet-testing-agent-skills/tree/HEAD/skills/dotnet-testing-awesome-assertions-guide

Skill Files

Browse the full folder contents for dotnet-testing-awesome-assertions-guide.

Download Skill

Loading file tree…

skills/dotnet-testing-awesome-assertions-guide/SKILL.md

Skill Metadata

Name
dotnet-testing-awesome-assertions-guide
Description
|

AwesomeAssertions 流暢斷言指南

本技能提供使用 AwesomeAssertions 進行高品質測試斷言的完整指南,涵蓋基礎語法、進階技巧與最佳實踐。

關於 AwesomeAssertions

AwesomeAssertions 是 FluentAssertions 的社群分支版本,使用 Apache 2.0 授權,完全免費且無商業使用限制。

核心特色

  • 完全免費:Apache 2.0 授權,適合商業專案使用
  • 流暢語法:支援方法鏈結的自然語言風格
  • 豐富斷言:涵蓋物件、集合、字串、數值、例外等各種類型
  • 優秀錯誤訊息:提供詳細且易理解的失敗資訊
  • 高性能:優化的實作確保測試執行效率
  • 可擴展:支援自訂 Assertions 方法

與 FluentAssertions 的關係

AwesomeAssertions 是 FluentAssertions 的社群 fork,主要差異:

| 項目 | FluentAssertions | AwesomeAssertions | | -------------- | ------------------ | ---------------------- | | 授權 | 商業專案需付費 | Apache 2.0(完全免費) | | 命名空間 | FluentAssertions | AwesomeAssertions | | API 相容性 | 原版 | 高度相容 | | 社群支援 | 官方維護 | 社群維護 |


安裝與設定

NuGet 套件安裝

# .NET CLI
dotnet add package AwesomeAssertions

# Package Manager Console
Install-Package AwesomeAssertions

csproj 設定(推薦)

<ItemGroup>
  <PackageReference Include="AwesomeAssertions" Version="9.4.0" PrivateAssets="all" />
</ItemGroup>

命名空間引用

using AwesomeAssertions;
using Xunit;

核心 Assertions 語法

所有 Assertions 皆以 .Should() 開始,搭配流暢方法鏈結。

| 類別 | 常用方法 | 說明 | |------|----------|------| | 物件 | NotBeNull(), BeOfType<T>(), BeEquivalentTo() | 空值、類型、相等性檢查 | | 字串 | Contain(), StartWith(), MatchRegex(), BeEquivalentTo() | 內容、模式、忽略大小寫比對 | | 數值 | BeGreaterThan(), BeInRange(), BeApproximately() | 比較、範圍、浮點精度 | | 數值集合 | EqualApproximately(), NotEqualApproximately() | 數值集合近似比對(9.4+) | | 集合 | HaveCount(), Contain(), BeEquivalentTo(), AllSatisfy() | 數量、內容、順序、條件 | | 例外 | Throw<T>(), NotThrow(), WithMessage(), WithInnerException() | 例外類型、訊息、巢狀例外 | | 非同步 | ThrowAsync<T>(), CompleteWithinAsync() | 非同步例外與完成驗證 |

完整語法範例與程式碼請參閱 references/core-assertions-syntax.md


進階技巧:複雜物件比對

使用 BeEquivalentTo() 搭配 options 進行深度物件比較:

  • 排除屬性options.Excluding(u => u.Id) — 排除自動生成欄位
  • 動態排除options.Excluding(ctx => ctx.Path.EndsWith("At")) — 按模式排除
  • 循環參考options.IgnoringCyclicReferences().WithMaxRecursionDepth(10)

進階技巧:自訂 Assertions 擴展

建立領域特定擴展方法,如 product.Should().BeValidProduct(),以及可重用排除擴展如 ExcludingAuditFields()

參考 templates/custom-assertions-template.cs 瞭解完整實作。

完整範例請參閱 references/complex-object-assertions.md


效能最佳化策略

  • 大量資料:先用 HaveCount() 快速檢查數量,再抽樣驗證(避免全量 BeEquivalentTo
  • 選擇性比對:使用匿名物件 + ExcludingMissingMembers() 只驗證關鍵屬性
// 選擇性屬性比對 — 只驗證關鍵欄位
order.Should().BeEquivalentTo(new
{
    CustomerId = 123,
    TotalAmount = 999.99m,
    Status = "Pending"
}, options => options.ExcludingMissingMembers());

9.2-9.4 版本新功能

數值集合近似比對 — [Not]EqualApproximately(9.4+)

針對 INumber<T> 數值集合,新增近似相等斷言(需 .NET 8+):

// 驗證數值集合在容差範圍內近似相等
var actual = new[] { 1.001f, 2.002f, 3.003f };
actual.Should().EqualApproximately(new[] { 1f, 2f, 3f }, 0.01f);

// 驗證集合不在容差範圍內近似相等
actual.Should().NotEqualApproximately(new[] { 10f, 20f, 30f }, 0.01f);

BeApproximately() 的差異BeApproximately() 用於單一數值,EqualApproximately() 用於數值集合,會逐一比對每個元素。當容差為 0 時,EqualApproximately 的行為等同於 Equal

集合格式化最大項目數設定(9.4+)

斷言失敗時,集合預設顯示前 32 個項目。現在可透過 FormattingOptions 自訂:

// 在 AssertionScope 中設定集合顯示上限
using var scope = new AssertionScope();
scope.FormattingOptions.MaxItems = 100; // 預設為 32

largeCollection.Should().BeEquivalentTo(expected);

也可透過自訂 Formatter 覆寫:

class LargeCollectionFormatter : EnumerableValueFormatter
{
    protected override int MaxItems => 100;
    public override bool CanHandle(object value) => value is IEnumerable<MyEntity>;
}

集合出現次數約束(9.2+)

Contain 方法新增出現次數約束:

var numbers = new[] { 1, 2, 2, 3, 3, 3 };
numbers.Should().Contain(2, AtLeast.Once());
numbers.Should().Contain(3, Exactly.Times(3));

Null 安全性改善 — [return: NotNull](9.4+)

所有 Should() 和斷言方法加入 [return: NotNull] 標註,讓編譯器的 nullable 分析正確追蹤:

// 9.4 前:Should() 後仍視為可能 null,需加 ! 運算子
var user = GetUser();
user.Should().NotBeNull();
user!.Name.Should().Be("test"); // 需要 ! 消除警告

// 9.4 後:Should().NotBeNull() 返回帶 [NotNull] 標註,編譯器自動推斷非 null
user.Should().NotBeNull();
user.Name.Should().Be("test"); // 不再需要 !

排除成員名稱(9.3+)

BeEquivalentTo 支援按名稱排除成員:

actual.Should().BeEquivalentTo(expected, options => options
    .Excluding(ctx => ctx.Name == "InternalState"));

最佳實踐與團隊標準

測試命名規範

遵循 方法_情境_預期結果 模式(如 CreateUser_有效電子郵件_應回傳啟用的使用者)。

錯誤訊息優化

在斷言中加入 because 字串,提供清晰的失敗上下文:

result.IsSuccess.Should().BeFalse("because negative payment amounts are not allowed");

AssertionScope 使用

使用 AssertionScope 收集多個失敗訊息,一次顯示所有問題:

using (new AssertionScope())
{
    user.Should().NotBeNull("User creation should not fail");
    user.Id.Should().BeGreaterThan(0, "User should have valid ID");
    user.Email.Should().NotBeNullOrEmpty("Email is required");
}

常見情境與解決方案

| 情境 | 關鍵技巧 | |------|----------| | API 回應驗證 | BeEquivalentTo() + Including() 選擇性比對 | | 資料庫實體驗證 | BeEquivalentTo() + Excluding() 排除自動生成欄位 | | 事件驗證 | 訂閱捕獲事件後逐一驗證屬性 |

完整程式碼範例請參閱 references/common-scenarios.md


疑難排解

問題 1:BeEquivalentTo 失敗但物件看起來相同

原因:可能包含自動生成欄位或時間戳記

解決方案

// 排除動態欄位
actual.Should().BeEquivalentTo(expected, options => options
    .Excluding(x => x.Id)
    .Excluding(x => x.CreatedAt)
    .Excluding(x => x.UpdatedAt)
);

問題 2:集合順序不同導致失敗

原因:集合順序不同

解決方案

// 使用 BeEquivalentTo 忽略順序
actual.Should().BeEquivalentTo(expected); // 不檢查順序

// 或明確指定需要檢查順序
actual.Should().Equal(expected); // 檢查順序

問題 3:浮點數比較失敗

原因:浮點數精度問題

解決方案

// 單一數值使用精度容差
actualValue.Should().BeApproximately(expectedValue, 0.001);

// 數值集合使用 EqualApproximately(9.4+,需 .NET 8+)
actualValues.Should().EqualApproximately(expectedValues, 0.001);

何時使用此技能

適用情境

撰寫單元測試或整合測試時 需要驗證複雜物件結構時 比對 API 回應或資料庫實體時 需要清晰的失敗訊息時 建立領域特定測試標準時

不適用情境

效能測試(使用專用 benchmarking 工具) 負載測試(使用 K6、JMeter 等) UI 測試(使用 Playwright、Selenium)


與其他技能的配合

與 unit-test-fundamentals 搭配

先使用 unit-test-fundamentals 建立測試結構,再使用本技能撰寫斷言:

[Fact]
public void Calculator_Add_兩個正數_應回傳總和()
{
    // Arrange - 遵循 3A Pattern
    var calculator = new Calculator();
    
    // Act
    var result = calculator.Add(2, 3);
    
    // Assert - 使用 AwesomeAssertions
    result.Should().Be(5);
}

與 test-naming-conventions 搭配

使用 test-naming-conventions 的命名規範,搭配本技能的斷言:

[Fact]
public void CreateUser_有效資料_應回傳啟用使用者()
{
    var user = userService.CreateUser("test@example.com");
    
    user.Should().NotBeNull()
        .And.BeOfType<User>();
    user.IsActive.Should().BeTrue();
}

與 xunit-project-setup 搭配

xunit-project-setup 建立的專案中安裝並使用 AwesomeAssertions。


輸出格式

  • 產生使用 AwesomeAssertions 流暢語法的測試斷言
  • 使用 Should().Be/BeEquivalentTo/Contain 等方法鏈
  • 包含物件比對、集合驗證、例外斷言範例
  • 提供 .csproj 套件參考(AwesomeAssertions)

參考資源

原始文章

本技能內容提煉自「老派軟體工程師的測試修練 - 30 天挑戰」系列文章:

  • Day 04 - AwesomeAssertions 基礎應用與實戰技巧

    • 鐵人賽文章:https://ithelp.ithome.com.tw/articles/10374188
    • 範例程式碼:https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day04
  • Day 05 - AwesomeAssertions 進階技巧與複雜情境應用

    • 鐵人賽文章:https://ithelp.ithome.com.tw/articles/10374425
    • 範例程式碼:https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day05

官方資源

  • AwesomeAssertions GitHub:https://github.com/AwesomeAssertions/AwesomeAssertions
  • AwesomeAssertions 官方文件:https://awesomeassertions.org/

相關文章

  • Fluent Assertions 授權變化討論:https://www.dotblogs.com.tw/mrkt/2025/04/19/152408

總結

AwesomeAssertions 提供了強大且可讀的斷言語法,是撰寫高品質測試的重要工具。透過:

  1. 流暢語法:讓測試程式碼更易讀
  2. 豐富斷言:涵蓋各種資料類型(9.4 新增數值集合近似比對)
  3. 自訂擴展:建立領域特定斷言
  4. 效能優化:處理大量資料情境(9.4 新增集合顯示上限設定)
  5. 完全免費:Apache 2.0 授權無商業限制
  6. Null 安全:9.4 起 [return: NotNull] 改善 nullable 分析

記住:好的斷言不僅能驗證結果,更能清楚表達預期行為,並在失敗時提供有用的診斷資訊。

參考 templates/assertion-examples.cs 查看更多實用範例。

AwesomeAssertions 流暢斷言指南 Skill | Agent Skills