{"id":"f78fd526-f740-42b7-bd33-570b875d1ba4","shortId":"dmQ3Ls","kind":"skill","title":"dotnet-testing-awesome-assertions-guide","tagline":"使用 AwesomeAssertions 進行流暢且可讀的測試斷言技能。當需要撰寫清晰的斷言、比對物件、驗證集合、處理複雜比對時使用。涵蓋 Should()、BeEquivalentTo()、Contain()、ThrowAsync() 等完整 API。\nMake sure to use this skill whenever the user mentions assertions, Should(), BeEquivalentTo, fluent assertions, AwesomeAssertions, or wants more readabl","description":"# AwesomeAssertions 流暢斷言指南\n\n本技能提供使用 AwesomeAssertions 進行高品質測試斷言的完整指南，涵蓋基礎語法、進階技巧與最佳實踐。\n\n## 關於 AwesomeAssertions\n\n**AwesomeAssertions** 是 FluentAssertions 的社群分支版本，使用 **Apache 2.0** 授權，完全免費且無商業使用限制。\n\n### 核心特色\n\n- **完全免費**：Apache 2.0 授權，適合商業專案使用\n- **流暢語法**：支援方法鏈結的自然語言風格\n- **豐富斷言**：涵蓋物件、集合、字串、數值、例外等各種類型\n- **優秀錯誤訊息**：提供詳細且易理解的失敗資訊\n- **高性能**：優化的實作確保測試執行效率\n- **可擴展**：支援自訂 Assertions 方法\n\n### 與 FluentAssertions 的關係\n\nAwesomeAssertions 是 FluentAssertions 的社群 fork，主要差異：\n\n| 項目           | FluentAssertions   | AwesomeAssertions      |\n| -------------- | ------------------ | ---------------------- |\n| **授權**       | 商業專案需付費     | Apache 2.0（完全免費） |\n| **命名空間**   | `FluentAssertions` | `AwesomeAssertions`    |\n| **API 相容性** | 原版               | 高度相容               |\n| **社群支援**   | 官方維護           | 社群維護               |\n\n---\n\n## 安裝與設定\n\n### NuGet 套件安裝\n\n```bash\n# .NET CLI\ndotnet add package AwesomeAssertions\n\n# Package Manager Console\nInstall-Package AwesomeAssertions\n```\n\n### csproj 設定（推薦）\n\n```xml\n<ItemGroup>\n  <PackageReference Include=\"AwesomeAssertions\" Version=\"9.4.0\" PrivateAssets=\"all\" />\n</ItemGroup>\n```\n\n### 命名空間引用\n\n```csharp\nusing AwesomeAssertions;\nusing Xunit;\n```\n\n---\n\n## 核心 Assertions 語法\n\n所有 Assertions 皆以 `.Should()` 開始，搭配流暢方法鏈結。\n\n| 類別 | 常用方法 | 說明 |\n|------|----------|------|\n| **物件** | `NotBeNull()`, `BeOfType<T>()`, `BeEquivalentTo()` | 空值、類型、相等性檢查 |\n| **字串** | `Contain()`, `StartWith()`, `MatchRegex()`, `BeEquivalentTo()` | 內容、模式、忽略大小寫比對 |\n| **數值** | `BeGreaterThan()`, `BeInRange()`, `BeApproximately()` | 比較、範圍、浮點精度 |\n| **數值集合** | `EqualApproximately()`, `NotEqualApproximately()` | 數值集合近似比對（9.4+） |\n| **集合** | `HaveCount()`, `Contain()`, `BeEquivalentTo()`, `AllSatisfy()` | 數量、內容、順序、條件 |\n| **例外** | `Throw<T>()`, `NotThrow()`, `WithMessage()`, `WithInnerException()` | 例外類型、訊息、巢狀例外 |\n| **非同步** | `ThrowAsync<T>()`, `CompleteWithinAsync()` | 非同步例外與完成驗證 |\n\n> 完整語法範例與程式碼請參閱 [references/core-assertions-syntax.md](references/core-assertions-syntax.md)\n\n---\n\n## 進階技巧：複雜物件比對\n\n使用 `BeEquivalentTo()` 搭配 `options` 進行深度物件比較：\n\n- **排除屬性**：`options.Excluding(u => u.Id)` — 排除自動生成欄位\n- **動態排除**：`options.Excluding(ctx => ctx.Path.EndsWith(\"At\"))` — 按模式排除\n- **循環參考**：`options.IgnoringCyclicReferences().WithMaxRecursionDepth(10)`\n\n---\n\n## 進階技巧：自訂 Assertions 擴展\n\n建立領域特定擴展方法，如 `product.Should().BeValidProduct()`，以及可重用排除擴展如 `ExcludingAuditFields()`。\n\n參考 [templates/custom-assertions-template.cs](templates/custom-assertions-template.cs) 瞭解完整實作。\n\n> 完整範例請參閱 [references/complex-object-assertions.md](references/complex-object-assertions.md)\n\n---\n\n## 效能最佳化策略\n\n- **大量資料**：先用 `HaveCount()` 快速檢查數量，再抽樣驗證（避免全量 `BeEquivalentTo`）\n- **選擇性比對**：使用匿名物件 + `ExcludingMissingMembers()` 只驗證關鍵屬性\n\n```csharp\n// 選擇性屬性比對 — 只驗證關鍵欄位\norder.Should().BeEquivalentTo(new\n{\n    CustomerId = 123,\n    TotalAmount = 999.99m,\n    Status = \"Pending\"\n}, options => options.ExcludingMissingMembers());\n```\n\n---\n\n## 9.2-9.4 版本新功能\n\n### 數值集合近似比對 — `[Not]EqualApproximately`（9.4+）\n\n針對 `INumber<T>` 數值集合，新增近似相等斷言（需 .NET 8+）：\n\n```csharp\n// 驗證數值集合在容差範圍內近似相等\nvar actual = new[] { 1.001f, 2.002f, 3.003f };\nactual.Should().EqualApproximately(new[] { 1f, 2f, 3f }, 0.01f);\n\n// 驗證集合不在容差範圍內近似相等\nactual.Should().NotEqualApproximately(new[] { 10f, 20f, 30f }, 0.01f);\n```\n\n> **與 `BeApproximately()` 的差異**：`BeApproximately()` 用於單一數值，`EqualApproximately()` 用於數值集合，會逐一比對每個元素。當容差為 0 時，`EqualApproximately` 的行為等同於 `Equal`。\n\n### 集合格式化最大項目數設定（9.4+）\n\n斷言失敗時，集合預設顯示前 32 個項目。現在可透過 `FormattingOptions` 自訂：\n\n```csharp\n// 在 AssertionScope 中設定集合顯示上限\nusing var scope = new AssertionScope();\nscope.FormattingOptions.MaxItems = 100; // 預設為 32\n\nlargeCollection.Should().BeEquivalentTo(expected);\n```\n\n也可透過自訂 Formatter 覆寫：\n\n```csharp\nclass LargeCollectionFormatter : EnumerableValueFormatter\n{\n    protected override int MaxItems => 100;\n    public override bool CanHandle(object value) => value is IEnumerable<MyEntity>;\n}\n```\n\n### 集合出現次數約束（9.2+）\n\n`Contain` 方法新增出現次數約束：\n\n```csharp\nvar numbers = new[] { 1, 2, 2, 3, 3, 3 };\nnumbers.Should().Contain(2, AtLeast.Once());\nnumbers.Should().Contain(3, Exactly.Times(3));\n```\n\n### Null 安全性改善 — `[return: NotNull]`（9.4+）\n\n所有 `Should()` 和斷言方法加入 `[return: NotNull]` 標註，讓編譯器的 nullable 分析正確追蹤：\n\n```csharp\n// 9.4 前：Should() 後仍視為可能 null，需加 ! 運算子\nvar user = GetUser();\nuser.Should().NotBeNull();\nuser!.Name.Should().Be(\"test\"); // 需要 ! 消除警告\n\n// 9.4 後：Should().NotBeNull() 返回帶 [NotNull] 標註，編譯器自動推斷非 null\nuser.Should().NotBeNull();\nuser.Name.Should().Be(\"test\"); // 不再需要 !\n```\n\n### 排除成員名稱（9.3+）\n\n`BeEquivalentTo` 支援按名稱排除成員：\n\n```csharp\nactual.Should().BeEquivalentTo(expected, options => options\n    .Excluding(ctx => ctx.Name == \"InternalState\"));\n```\n\n---\n\n## 最佳實踐與團隊標準\n\n### 測試命名規範\n\n遵循 `方法_情境_預期結果` 模式（如 `CreateUser_有效電子郵件_應回傳啟用的使用者`）。\n\n### 錯誤訊息優化\n\n在斷言中加入 `because` 字串，提供清晰的失敗上下文：\n\n```csharp\nresult.IsSuccess.Should().BeFalse(\"because negative payment amounts are not allowed\");\n```\n\n### AssertionScope 使用\n\n使用 `AssertionScope` 收集多個失敗訊息，一次顯示所有問題：\n\n```csharp\nusing (new AssertionScope())\n{\n    user.Should().NotBeNull(\"User creation should not fail\");\n    user.Id.Should().BeGreaterThan(0, \"User should have valid ID\");\n    user.Email.Should().NotBeNullOrEmpty(\"Email is required\");\n}\n```\n\n---\n\n## 常見情境與解決方案\n\n| 情境 | 關鍵技巧 |\n|------|----------|\n| API 回應驗證 | `BeEquivalentTo()` + `Including()` 選擇性比對 |\n| 資料庫實體驗證 | `BeEquivalentTo()` + `Excluding()` 排除自動生成欄位 |\n| 事件驗證 | 訂閱捕獲事件後逐一驗證屬性 |\n\n> 完整程式碼範例請參閱 [references/common-scenarios.md](references/common-scenarios.md)\n\n---\n\n## 疑難排解\n\n### 問題 1：BeEquivalentTo 失敗但物件看起來相同\n\n**原因**：可能包含自動生成欄位或時間戳記\n\n**解決方案**：\n\n```csharp\n// 排除動態欄位\nactual.Should().BeEquivalentTo(expected, options => options\n    .Excluding(x => x.Id)\n    .Excluding(x => x.CreatedAt)\n    .Excluding(x => x.UpdatedAt)\n);\n```\n\n### 問題 2：集合順序不同導致失敗\n\n**原因**：集合順序不同\n\n**解決方案**：\n\n```csharp\n// 使用 BeEquivalentTo 忽略順序\nactual.Should().BeEquivalentTo(expected); // 不檢查順序\n\n// 或明確指定需要檢查順序\nactual.Should().Equal(expected); // 檢查順序\n```\n\n### 問題 3：浮點數比較失敗\n\n**原因**：浮點數精度問題\n\n**解決方案**：\n\n```csharp\n// 單一數值使用精度容差\nactualValue.Should().BeApproximately(expectedValue, 0.001);\n\n// 數值集合使用 EqualApproximately（9.4+，需 .NET 8+）\nactualValues.Should().EqualApproximately(expectedValues, 0.001);\n```\n\n---\n\n## 何時使用此技能\n\n### 適用情境\n\n撰寫單元測試或整合測試時\n需要驗證複雜物件結構時\n比對 API 回應或資料庫實體時\n需要清晰的失敗訊息時\n建立領域特定測試標準時\n\n### 不適用情境\n\n效能測試（使用專用 benchmarking 工具）\n負載測試（使用 K6、JMeter 等）\nUI 測試（使用 Playwright、Selenium）\n\n---\n\n## 與其他技能的配合\n\n### 與 unit-test-fundamentals 搭配\n\n先使用 `unit-test-fundamentals` 建立測試結構，再使用本技能撰寫斷言：\n\n```csharp\n[Fact]\npublic void Calculator_Add_兩個正數_應回傳總和()\n{\n    // Arrange - 遵循 3A Pattern\n    var calculator = new Calculator();\n    \n    // Act\n    var result = calculator.Add(2, 3);\n    \n    // Assert - 使用 AwesomeAssertions\n    result.Should().Be(5);\n}\n```\n\n### 與 test-naming-conventions 搭配\n\n使用 `test-naming-conventions` 的命名規範，搭配本技能的斷言：\n\n```csharp\n[Fact]\npublic void CreateUser_有效資料_應回傳啟用使用者()\n{\n    var user = userService.CreateUser(\"test@example.com\");\n    \n    user.Should().NotBeNull()\n        .And.BeOfType<User>();\n    user.IsActive.Should().BeTrue();\n}\n```\n\n### 與 xunit-project-setup 搭配\n\n在 `xunit-project-setup` 建立的專案中安裝並使用 AwesomeAssertions。\n\n---\n\n## 輸出格式\n\n- 產生使用 AwesomeAssertions 流暢語法的測試斷言\n- 使用 Should().Be/BeEquivalentTo/Contain 等方法鏈\n- 包含物件比對、集合驗證、例外斷言範例\n- 提供 .csproj 套件參考（AwesomeAssertions）\n\n## 參考資源\n\n### 原始文章\n\n本技能內容提煉自「老派軟體工程師的測試修練 - 30 天挑戰」系列文章：\n\n- **Day 04 - AwesomeAssertions 基礎應用與實戰技巧**\n  - 鐵人賽文章：https://ithelp.ithome.com.tw/articles/10374188\n  - 範例程式碼：https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day04\n\n- **Day 05 - AwesomeAssertions 進階技巧與複雜情境應用**\n  - 鐵人賽文章：https://ithelp.ithome.com.tw/articles/10374425\n  - 範例程式碼：https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day05\n\n### 官方資源\n\n- **AwesomeAssertions GitHub**：https://github.com/AwesomeAssertions/AwesomeAssertions\n- **AwesomeAssertions 官方文件**：https://awesomeassertions.org/\n\n### 相關文章\n\n- **Fluent Assertions 授權變化討論**：https://www.dotblogs.com.tw/mrkt/2025/04/19/152408\n\n---\n\n## 總結\n\nAwesomeAssertions 提供了強大且可讀的斷言語法，是撰寫高品質測試的重要工具。透過：\n\n1. **流暢語法**：讓測試程式碼更易讀\n2. **豐富斷言**：涵蓋各種資料類型（9.4 新增數值集合近似比對）\n3. **自訂擴展**：建立領域特定斷言\n4. **效能優化**：處理大量資料情境（9.4 新增集合顯示上限設定）\n5. **完全免費**：Apache 2.0 授權無商業限制\n6. **Null 安全**：9.4 起 `[return: NotNull]` 改善 nullable 分析\n\n記住：好的斷言不僅能驗證結果，更能清楚表達預期行為，並在失敗時提供有用的診斷資訊。\n\n參考 [templates/assertion-examples.cs](templates/assertion-examples.cs) 查看更多實用範例。","tags":["dotnet","testing","awesome","assertions","guide","agent","skills","kevintsengtw","agent-skills","ai-assisted-development","copilot-skills","csharp"],"capabilities":["skill","source-kevintsengtw","skill-dotnet-testing-awesome-assertions-guide","topic-agent-skills","topic-ai-assisted-development","topic-copilot-skills","topic-csharp","topic-dotnet","topic-dotnet-testing","topic-github-copilot","topic-integration-testing","topic-testing","topic-unit-testing","topic-xunit"],"categories":["dotnet-testing-agent-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/kevintsengtw/dotnet-testing-agent-skills/dotnet-testing-awesome-assertions-guide","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add kevintsengtw/dotnet-testing-agent-skills","source_repo":"https://github.com/kevintsengtw/dotnet-testing-agent-skills","install_from":"skills.sh"}},"qualityScore":"0.461","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 23 github stars · SKILL.md body (8,294 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-24T13:02:26.452Z","embedding":null,"createdAt":"2026-04-18T23:04:26.425Z","updatedAt":"2026-04-24T13:02:26.452Z","lastSeenAt":"2026-04-24T13:02:26.452Z","tsv":"'-9.4':265 '/articles/10374188':726 '/articles/10374425':738 '/awesomeassertions/awesomeassertions':748 '/kevintsengtw/30days_in_testing_samples/tree/main/day04':730 '/kevintsengtw/30days_in_testing_samples/tree/main/day05':742 '/mrkt/2025/04/19/152408':758 '0':315,496 '0.001':578,588 '0.01':295,304 '04':720 '05':732 '1':374,526,764 '1.001':283 '10':219 '100':339,356 '10f':301 '123':256 '1f':292 '2':375,376,382,549,647,767 '2.0':56,62,96,783 '2.002':285 '20f':302 '2f':293 '3':377,378,379,386,388,568,648,772 '3.003':287 '30':716 '30f':303 '32':324,341 '3a':637 '3f':294 '4':775 '5':654,780 '6':785 '8':277,584 '9.2':264,367 '9.3':438 '9.4':173,270,321,393,404,422,581,770,778,788 '999.99':258 'act':643 'actual':281 'actual.should':289,298,442,534,558,563 'actualvalue.should':575 'actualvalues.should':585 'add':115,632 'allow':476 'allsatisfi':178 'amount':473 'and.beoftype':681 'apach':55,61,95,782 'api':20,101,510,594 'arrang':635 'assert':5,31,35,79,136,139,222,649,754 'assertionscop':331,337,477,480,486 'atleast.once':383 'awesom':4 'awesomeassert':8,36,41,44,49,50,84,92,100,117,124,132,651,696,699,711,721,733,744,749,760 'awesomeassertions.org':751 'bash':111 'be/beequivalentto/contain':703 'beapproxim':165,307,309,576 'beequivalentto':16,33,150,158,177,201,244,253,343,439,443,512,516,527,535,556,559 'befals':469 'begreaterthan':163,495 'beinrang':164 'benchmark':601 'beoftyp':149 'betru':683 'bevalidproduct':227 'bool':359 'calcul':631,640,642 'calculator.add':646 'canhandl':360 'class':349 'cli':113 'completewithinasync':193 'consol':120 'contain':17,155,176,368,381,385 'convent':659,665 'createus':459,672 'creation':490 'csharp':130,249,278,329,348,370,403,441,467,483,532,554,573,627,668 'csproj':125,709 'ctx':212,448 'ctx.name':449 'ctx.path.endswith':213 'customerid':255 'day':719,731 'dotnet':2,114 'dotnet-testing-awesome-assertions-guid':1 'email':504 'enumerablevalueformatt':351 'equal':319,564 'equalapproxim':170,269,290,311,317,580,586 'exactly.times':387 'exclud':447,517,539,542,545 'excludingauditfield':229 'excludingmissingmemb':247 'expect':344,444,536,560,565 'expectedvalu':577,587 'f':284,286,288,296,305 'fact':628,669 'fail':493 'fluent':34,753 'fluentassert':52,82,86,91,99 'fork':88 'formatt':346 'formattingopt':327 'fundament':618,624 'getus':413 'github':745 'github.com':729,741,747 'github.com/awesomeassertions/awesomeassertions':746 'github.com/kevintsengtw/30days_in_testing_samples/tree/main/day04':728 'github.com/kevintsengtw/30days_in_testing_samples/tree/main/day05':740 'guid':6 'havecount':175,240 'id':501 'ienumer':365 'includ':513 'instal':122 'install-packag':121 'int':354 'internalst':450 'inumb':272 'ithelp.ithome.com.tw':725,737 'ithelp.ithome.com.tw/articles/10374188':724 'ithelp.ithome.com.tw/articles/10374425':736 'jmeter':606 'k6':605 'largecollection.should':342 'largecollectionformatt':350 'm':259 'make':21 'manag':119 'matchregex':157 'maxitem':355 'mention':30 'name':658,664 'name.should':417 'negat':471 'net':112,276,583 'new':254,282,291,300,336,373,485,641 'notbenul':148,415,425,432,488,680 'notbenullorempti':503 'notequalapproxim':171,299 'notnul':392,398,427,791 'notthrow':185 'nuget':109 'null':389,408,430,786 'nullabl':401,793 'number':372 'numbers.should':380,384 'object':361 'option':203,262,445,446,537,538 'options.excluding':206,211 'options.excludingmissingmembers':263 'options.ignoringcyclicreferences':217 'order.should':252 'overrid':353,358 'packag':116,118,123 'pattern':638 'payment':472 'pend':261 'playwright':611 'product.should':226 'project':687,693 'protect':352 'public':357,629,670 'readabl':40 'references/common-scenarios.md':522,523 'references/complex-object-assertions.md':235,236 'references/core-assertions-syntax.md':196,197 'requir':506 'result':645 'result.issuccess.should':468 'result.should':652 'return':391,397,790 'scope':335 'scope.formattingoptions.maxitems':338 'selenium':612 'setup':688,694 'skill':26 'skill-dotnet-testing-awesome-assertions-guide' 'source-kevintsengtw' 'startwith':156 'status':260 'sure':22 'templates/assertion-examples.cs':800,801 'templates/custom-assertions-template.cs':231,232 'test':3,419,435,617,623,657,663 'test-naming-convent':656,662 'test@example.com':678 'throw':184 'throwasync':18,192 'topic-agent-skills' 'topic-ai-assisted-development' 'topic-copilot-skills' 'topic-csharp' 'topic-dotnet' 'topic-dotnet-testing' 'topic-github-copilot' 'topic-integration-testing' 'topic-testing' 'topic-unit-testing' 'topic-xunit' 'totalamount':257 'u':207 'u.id':208 'ui':608 'unit':616,622 'unit-test-fundament':615,621 'use':24,131,133,333,484 'user':29,412,416,489,497,676 'user.email.should':502 'user.id.should':494 'user.isactive.should':682 'user.name.should':433 'user.should':414,431,487,679 'userservice.createuser':677 'valid':500 'valu':362,363 'var':280,334,371,411,639,644,675 'void':630,671 'want':38 'whenev':27 'withinnerexcept':187 'withmaxrecursiondepth':218 'withmessag':186 'www.dotblogs.com.tw':757 'www.dotblogs.com.tw/mrkt/2025/04/19/152408':756 'x':540,543,546 'x.createdat':544 'x.id':541 'x.updatedat':547 'xml':128 'xunit':134,686,692 'xunit-project-setup':685,691 '一次顯示所有問題':482 '不再需要':436 '不檢查順序':561 '不適用情境':598 '並在失敗時提供有用的診斷資訊':798 '中設定集合顯示上限':332 '主要差異':89 '也可透過自訂':345 '事件驗證':519 '以及可重用排除擴展如':228 '何時使用此技能':589 '使用':7,54,200,478,479,555,604,610,650,661,701 '使用匿名物件':246 '使用專用':600 '例外':183 '例外斷言範例':707 '例外等各種類型':72 '例外類型':188 '個項目':325 '優化的實作確保測試執行效率':76 '優秀錯誤訊息':73 '先使用':620 '先用':239 '內容':159,180 '兩個正數':633 '再使用本技能撰寫斷言':626 '再抽樣驗證':242 '分析':794 '分析正確追蹤':402 '前':405 '動態排除':210 '包含物件比對':705 '原因':529,551,570 '原始文章':713 '原版':103 '參考':230,799 '參考資源':712 '只驗證關鍵屬性':248 '只驗證關鍵欄位':251 '可擴展':77 '可能包含自動生成欄位或時間戳記':530 '命名空間':98 '命名空間引用':129 '和斷言方法加入':396 '商業專案需付費':94 '問題':525,548,567 '單一數值使用精度容差':574 '回應或資料庫實體時':595 '回應驗證':511 '在':330,690 '在斷言中加入':463 '基礎應用與實戰技巧':722 '大量資料':238 '天挑戰':717 '失敗但物件看起來相同':528 '套件參考':710 '套件安裝':110 '好的斷言不僅能驗證結果':796 '如':225,458 '字串':70,154,465 '安全':787 '安全性改善':390 '安裝與設定':108 '完全免費':60,97,781 '完全免費且無商業使用限制':58 '完整程式碼範例請參閱':521 '完整範例請參閱':234 '完整語法範例與程式碼請參閱':195 '官方文件':750 '官方維護':106 '官方資源':743 '巢狀例外':190 '工具':602 '常用方法':145 '常見情境與解決方案':507 '建立測試結構':625 '建立的專案中安裝並使用':695 '建立領域特定擴展方法':224 '建立領域特定斷言':774 '建立領域特定測試標準時':597 '後':423 '後仍視為可能':407 '循環參考':216 '快速檢查數量':241 '忽略大小寫比對':161 '忽略順序':557 '情境':455,508 '應回傳啟用使用者':674 '應回傳啟用的使用者':461 '應回傳總和':634 '或明確指定需要檢查順序':562 '所有':138,394 '按模式排除':215 '授權':57,63,93 '授權無商業限制':784 '授權變化討論':755 '排除動態欄位':533 '排除屬性':205 '排除成員名稱':437 '排除自動生成欄位':209,518 '推薦':127 '提供':708 '提供了強大且可讀的斷言語法':761 '提供清晰的失敗上下文':466 '提供詳細且易理解的失敗資訊':74 '搭配':202,619,660,689 '搭配本技能的斷言':667 '搭配流暢方法鏈結':143 '撰寫單元測試或整合測試時':591 '擴展':223 '支援按名稱排除成員':440 '支援方法鏈結的自然語言風格':66 '支援自訂':78 '收集多個失敗訊息':481 '改善':792 '效能優化':776 '效能最佳化策略':237 '效能測試':599 '數值':71,162 '數值集合':169,273 '數值集合使用':579 '數值集合近似比對':172,267 '數量':179 '新增數值集合近似比對':771 '新增近似相等斷言':274 '新增集合顯示上限設定':779 '斷言失敗時':322 '方法':80,454 '方法新增出現次數約束':369 '是':51,85 '是撰寫高品質測試的重要工具':762 '時':316 '更能清楚表達預期行為':797 '最佳實踐與團隊標準':451 '會逐一比對每個元素':313 '有效資料':673 '有效電子郵件':460 '本技能內容提煉自':714 '本技能提供使用':43 '查看更多實用範例':802 '核心':135 '核心特色':59 '條件':182 '標註':399,428 '模式':160,457 '檢查順序':566 '比對':593 '比對物件':11 '比較':166 '流暢斷言指南':42 '流暢語法':65,765 '流暢語法的測試斷言':700 '浮點數比較失敗':569 '浮點數精度問題':571 '浮點精度':168 '消除警告':421 '涵蓋':14 '涵蓋各種資料類型':769 '涵蓋基礎語法':46 '涵蓋物件':68 '測試':609 '測試命名規範':452 '版本新功能':266 '物件':147 '現在可透過':326 '產生使用':698 '用於單一數值':310 '用於數值集合':312 '當容差為':314 '當需要撰寫清晰的斷言':10 '疑難排解':524 '的命名規範':666 '的差異':308 '的社群':87 '的社群分支版本':53 '的行為等同於':318 '的關係':83 '皆以':140 '相容性':102 '相等性檢查':153 '相關文章':752 '瞭解完整實作':233 '社群支援':105 '社群維護':107 '空值':151 '等':607 '等完整':19 '等方法鏈':704 '範例程式碼':727,739 '範圍':167 '系列文章':718 '編譯器自動推斷非':429 '總結':759 '老派軟體工程師的測試修練':715 '自訂':221,328 '自訂擴展':773 '與':81,306,614,655,684 '與其他技能的配合':613 '處理大量資料情境':777 '處理複雜比對時使用':13 '複雜物件比對':199 '覆寫':347 '解決方案':531,553,572 '訂閱捕獲事件後逐一驗證屬性':520 '訊息':189 '記住':795 '設定':126 '語法':137 '說明':146 '讓測試程式碼更易讀':766 '讓編譯器的':400 '豐富斷言':67,768 '負載測試':603 '資料庫實體驗證':515 '起':789 '輸出格式':697 '返回帶':426 '透過':763 '進行流暢且可讀的測試斷言技能':9 '進行深度物件比較':204 '進行高品質測試斷言的完整指南':45 '進階技巧':198,220 '進階技巧與最佳實踐':47 '進階技巧與複雜情境應用':734 '運算子':410 '適合商業專案使用':64 '適用情境':590 '遵循':453,636 '選擇性屬性比對':250 '選擇性比對':245,514 '避免全量':243 '針對':271 '錯誤訊息優化':462 '鐵人賽文章':723,735 '開始':142 '關於':48 '關鍵技巧':509 '集合':69,174 '集合出現次數約束':366 '集合格式化最大項目數設定':320 '集合順序不同':552 '集合順序不同導致失敗':550 '集合預設顯示前':323 '集合驗證':706 '需':275,582 '需加':409 '需要':420 '需要清晰的失敗訊息時':596 '需要驗證複雜物件結構時':592 '非同步':191 '非同步例外與完成驗證':194 '項目':90 '順序':181 '預期結果':456 '預設為':340 '類別':144 '類型':152 '驗證數值集合在容差範圍內近似相等':279 '驗證集合':12 '驗證集合不在容差範圍內近似相等':297 '高度相容':104 '高性能':75","prices":[{"id":"e58de019-2c9e-48ca-bd37-a6c0b1b21e5b","listingId":"f78fd526-f740-42b7-bd33-570b875d1ba4","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"kevintsengtw","category":"dotnet-testing-agent-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T23:04:26.425Z"}],"sources":[{"listingId":"f78fd526-f740-42b7-bd33-570b875d1ba4","source":"github","sourceId":"kevintsengtw/dotnet-testing-agent-skills/dotnet-testing-awesome-assertions-guide","sourceUrl":"https://github.com/kevintsengtw/dotnet-testing-agent-skills/tree/main/skills/dotnet-testing-awesome-assertions-guide","isPrimary":false,"firstSeenAt":"2026-04-18T23:04:26.425Z","lastSeenAt":"2026-04-24T13:02:26.452Z"}],"details":{"listingId":"f78fd526-f740-42b7-bd33-570b875d1ba4","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"kevintsengtw","slug":"dotnet-testing-awesome-assertions-guide","github":{"repo":"kevintsengtw/dotnet-testing-agent-skills","stars":23,"topics":["agent-skills","ai-assisted-development","copilot-skills","csharp","dotnet","dotnet-testing","github-copilot","integration-testing","testing","unit-testing","xunit"],"license":"mit","html_url":"https://github.com/kevintsengtw/dotnet-testing-agent-skills","pushed_at":"2026-03-31T07:28:56Z","description":"AI Agent Skills for .NET Testing - Based on 30-Day Testing Challenge (iThome Ironman 2025 Winner)","skill_md_sha":"a30cbc2d988cacdb96ec02b65ebaf186c7da2014","skill_md_path":"skills/dotnet-testing-awesome-assertions-guide/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/kevintsengtw/dotnet-testing-agent-skills/tree/main/skills/dotnet-testing-awesome-assertions-guide"},"layout":"multi","source":"github","category":"dotnet-testing-agent-skills","frontmatter":{"name":"dotnet-testing-awesome-assertions-guide","description":"使用 AwesomeAssertions 進行流暢且可讀的測試斷言技能。當需要撰寫清晰的斷言、比對物件、驗證集合、處理複雜比對時使用。涵蓋 Should()、BeEquivalentTo()、Contain()、ThrowAsync() 等完整 API。\nMake sure to use this skill whenever the user mentions assertions, Should(), BeEquivalentTo, fluent assertions, AwesomeAssertions, or wants more readable test assertions, even if they don't explicitly ask for assertion guidance.\nKeywords: assertions, awesome assertions, fluent assertions, 斷言, 流暢斷言, Should(), Be(), BeEquivalentTo, Contain, ThrowAsync, NotBeNull, 物件比對, 集合驗證, 例外斷言, AwesomeAssertions, FluentAssertions, fluent syntax"},"skills_sh_url":"https://skills.sh/kevintsengtw/dotnet-testing-agent-skills/dotnet-testing-awesome-assertions-guide"},"updatedAt":"2026-04-24T13:02:26.452Z"}}