Use AwesomeAssertions to write fluent, readable test assertions. Use this skill when you need to write clear assertions, compare objects, validate collections, or handle complex comparisons. Covers the full API including Should(), BeEquivalentTo(), Contain(), ThrowAsync(), and more. Keywords: assertions, awesome assertions, fluent assertions, assertion, fluent assertion, Should(), Be(), BeEquivalentTo, Contain, ThrowAsync, NotBeNull, object comparison, collection validation, exception assertions, AwesomeAssertions, FluentAssertions, fluent syntax
本技能提供使用 AwesomeAssertions 進行高品質測試斷言的完整指南,涵蓋基礎語法、進階技巧與最佳實踐。
AwesomeAssertions 是 FluentAssertions 的社群分支版本,使用 Apache 2.0 授權,完全免費且無商業使用限制。
AwesomeAssertions 是 FluentAssertions 的社群 fork,主要差異:
| 項目 | FluentAssertions | AwesomeAssertions |
| -------------- | ------------------ | ---------------------- |
| 授權 | 商業專案需付費 | Apache 2.0(完全免費) |
| 命名空間 | FluentAssertions | AwesomeAssertions |
| API 相容性 | 原版 | 高度相容 |
| 社群支援 | 官方維護 | 社群維護 |
# .NET CLI
dotnet add package AwesomeAssertions
# Package Manager Console
Install-Package AwesomeAssertions
<ItemGroup>
<PackageReference Include="AwesomeAssertions" Version="9.4.0" PrivateAssets="all" />
</ItemGroup>
using AwesomeAssertions;
using Xunit;
所有 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)建立領域特定擴展方法,如 product.Should().BeValidProduct(),以及可重用排除擴展如 ExcludingAuditFields()。
參考 templates/custom-assertions-template.cs 瞭解完整實作。
HaveCount() 快速檢查數量,再抽樣驗證(避免全量 BeEquivalentTo)ExcludingMissingMembers() 只驗證關鍵屬性// 選擇性屬性比對 — 只驗證關鍵欄位
order.Should().BeEquivalentTo(new
{
CustomerId = 123,
TotalAmount = 999.99m,
Status = "Pending"
}, options => options.ExcludingMissingMembers());
[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。
斷言失敗時,集合預設顯示前 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>;
}
Contain 方法新增出現次數約束:
var numbers = new[] { 1, 2, 2, 3, 3, 3 };
numbers.Should().Contain(2, AtLeast.Once());
numbers.Should().Contain(3, Exactly.Times(3));
[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"); // 不再需要 !
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 收集多個失敗訊息,一次顯示所有問題:
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
原因:可能包含自動生成欄位或時間戳記
解決方案:
// 排除動態欄位
actual.Should().BeEquivalentTo(expected, options => options
.Excluding(x => x.Id)
.Excluding(x => x.CreatedAt)
.Excluding(x => x.UpdatedAt)
);
原因:集合順序不同
解決方案:
// 使用 BeEquivalentTo 忽略順序
actual.Should().BeEquivalentTo(expected); // 不檢查順序
// 或明確指定需要檢查順序
actual.Should().Equal(expected); // 檢查順序
原因:浮點數精度問題
解決方案:
// 單一數值使用精度容差
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 建立測試結構,再使用本技能撰寫斷言:
[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 的命名規範,搭配本技能的斷言:
[Fact]
public void CreateUser_有效資料_應回傳啟用使用者()
{
var user = userService.CreateUser("test@example.com");
user.Should().NotBeNull()
.And.BeOfType<User>();
user.IsActive.Should().BeTrue();
}
在 xunit-project-setup 建立的專案中安裝並使用 AwesomeAssertions。
本技能內容提煉自「老派軟體工程師的測試修練 - 30 天挑戰」系列文章:
Day 04 - AwesomeAssertions 基礎應用與實戰技巧
Day 05 - AwesomeAssertions 進階技巧與複雜情境應用
AwesomeAssertions 提供了強大且可讀的斷言語法,是撰寫高品質測試的重要工具。透過:
[return: NotNull] 改善 nullable 分析記住:好的斷言不僅能驗證結果,更能清楚表達預期行為,並在失敗時提供有用的診斷資訊。
參考 templates/assertion-examples.cs 查看更多實用範例。
npx skills add kevintsengtw/dotnet-测试-awesome-assertions-指南下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer