$npx -y skills add kevintsengtw/dotnet-testing-agent-skills --skill dotnet-testing-autofixture-basics使用 AutoFixture 自動產生測試資料的基礎技能。當需要快速產生測試物件、減少樣板程式碼、實現匿名測試時使用。涵蓋 Fixture.Create、CreateMany、循環參考處理、與 xUnit 整合等。 Make sure to use this skill whenever the user mentions AutoFixture, fixture.Create, anonymous testing, auto-generating test data, or reducing test boilerplate, even if they
| 1 | # AutoFixture 基礎:自動產生測試資料 |
| 2 | |
| 3 | ## 安裝套件 |
| 4 | |
| 5 | ```xml |
| 6 | <PackageReference Include="AutoFixture" Version="4.18.1" /> |
| 7 | <PackageReference Include="AutoFixture.Xunit2" Version="4.18.1" /> |
| 8 | ``` |
| 9 | |
| 10 | 或透過命令列安裝: |
| 11 | |
| 12 | ```powershell |
| 13 | dotnet add package AutoFixture |
| 14 | dotnet add package AutoFixture.Xunit2 |
| 15 | ``` |
| 16 | |
| 17 | ## 核心 API 摘要 |
| 18 | |
| 19 | | API | 用途 | 預設行為 | |
| 20 | | ------------------------ | ---------------------------- | --------------------- | |
| 21 | | `fixture.Create<T>()` | 產生單一物件 | 自動填充所有屬性 | |
| 22 | | `fixture.CreateMany<T>()` | 產生集合 | 預設 3 個元素 | |
| 23 | | `fixture.Build<T>()` | 精確控制屬性 | 搭配 With/Without | |
| 24 | | `OmitAutoProperties()` | 只設定必要屬性 | 其餘保持預設值 | |
| 25 | |
| 26 | ### 基本使用 |
| 27 | |
| 28 | `Fixture` 是核心類別,提供自動產生測試資料的能力: |
| 29 | |
| 30 | - **基本型別**:`int`(隨機正整數)、`string`(GUID 格式)、`decimal`、`bool`、`DateTime`、`Guid` |
| 31 | - **複雜物件**:自動建構巢狀物件與集合屬性,所有層級的屬性都會自動填入值 |
| 32 | - **集合產生**:`CreateMany<T>()` 預設產生 3 個元素,可指定數量如 `CreateMany<T>(10)` |
| 33 | |
| 34 | > 完整程式碼範例請參考 [references/basic-usage-examples.md](references/basic-usage-examples.md) |
| 35 | |
| 36 | ### Build<T>() 精確控制 |
| 37 | |
| 38 | 當需要對特定屬性進行控制時,使用 `Build<T>()` 模式: |
| 39 | |
| 40 | | 方法 | 用途 | 範例 | |
| 41 | | ----------------------- | ---------------------------- | ------------------------------------------ | |
| 42 | | `.With(prop, value)` | 指定固定值 | `.With(x => x.Name, "測試客戶")` | |
| 43 | | `.With(prop, factory)` | 使用工廠方法產生值 | `.With(x => x.Price, () => random.Next())` | |
| 44 | | `.Without(prop)` | 排除屬性,保持預設值 | `.Without(x => x.InternalId)` | |
| 45 | | `.OmitAutoProperties()` | 不自動設定任何屬性 | 只設定關心的屬性 | |
| 46 | |
| 47 | ### 循環參考處理 |
| 48 | |
| 49 | 當物件包含循環參考時(如 Category 的 Parent 屬性指向自己),AutoFixture 預設會拋出 `ObjectCreationException`。 |
| 50 | |
| 51 | **解決方案:** 使用 `OmitOnRecursionBehavior`,移除預設的 `ThrowingRecursionBehavior` 並加入忽略循環參考的行為。 |
| 52 | |
| 53 | **建議:** 建立 `AutoFixtureTestBase` 基底類別統一處理循環參考,避免在每個測試中重複設定。 |
| 54 | |
| 55 | > 完整程式碼範例請參考 [references/build-and-customization.md](references/build-and-customization.md) |
| 56 | |
| 57 | ## xUnit 整合 |
| 58 | |
| 59 | ### Fixture 共享客製化 |
| 60 | |
| 61 | 在測試類別建構函式中使用 `Customize<T>()` 設定共用的資料產生規則,所有測試方法共享同一個 Fixture 設定。 |
| 62 | |
| 63 | ### 結合 Theory 測試 |
| 64 | |
| 65 | 搭配 `[Theory]` + `[InlineData]`,使用 `Build<T>().With()` 針對不同輸入情境設定關鍵屬性值,驗證各情境的預期行為。 |
| 66 | |
| 67 | ### 匿名測試原則 |
| 68 | |
| 69 | 測試應該關注「行為」而不是「資料」: |
| 70 | |
| 71 | - **好的做法:** 使用 `fixture.Create<T>()` 產生任意有效資料,驗證操作結果 |
| 72 | - **避免:** 依賴隨機產生值的具體內容(如假設年齡大於 18) |
| 73 | - **正確:** 使用 `Build<T>().With()` 明確設定影響測試結果的關鍵值 |
| 74 | |
| 75 | > 完整程式碼範例請參考 [references/xunit-integration-examples.md](references/xunit-integration-examples.md) |
| 76 | |
| 77 | ## 進化比較:Test Data Builder vs AutoFixture |
| 78 | |
| 79 | | 層面 | Test Data Builder | AutoFixture | |
| 80 | | ---------- | ---------------------- | ------------------ | |
| 81 | | 程式碼行數 | 40+ 行 Builder + 測試 | 5 行測試 | |
| 82 | | 維護成本 | 物件改變需更新 Builder | 自動適應變化 | |
| 83 | | 開發時間 | 先寫 Builder 再寫測試 | 直接寫測試 | |
| 84 | | 大量資料 | 需要迴圈 | `CreateMany(100)` | |
| 85 | | 可讀性 | 業務語意明確 | 需理解 AutoFixture | |
| 86 | |
| 87 | > 完整比較程式碼與混合策略請參考 [references/xunit-integration-examples.md](references/xunit-integration-examples.md) |
| 88 | |
| 89 | ## 混合策略建議 |
| 90 | |
| 91 | 結合 Test Data Builder 與 AutoFixture 的優點: |
| 92 | |
| 93 | - 使用 AutoFixture 建立基礎資料,再用 Builder 加工 |
| 94 | - 使用 `TestDataFactory` 封裝常用的資料產生模式 |
| 95 | - 大量隨機資料產生使用 `CreateMany<T>(count)` |
| 96 | |
| 97 | > 完整比較程式碼與混合策略請參考 [references/xunit-integration-examples.md](references/xunit-integration-examples.md) |
| 98 | |
| 99 | ## 實務應用場景 |
| 100 | |
| 101 | AutoFixture 在實務中常用於以下場景: |
| 102 | |
| 103 | - **Entity 測試**:搭配 Theory 驗證不同輸入場景 |
| 104 | - **DTO 驗證**:使用 `Build<T>()` 產生符合驗證規則的資料 |
| 105 | - **大量資料測試**:使用 `CreateMany()` 產生批次資料 |
| 106 | - **服務層測試**:搭配 NSubstitute 自動產生 Mock 參數 |
| 107 | |
| 108 | > 完整程式碼範例請參閱 [references/practical-scenarios.md](references/practical-scenarios.md) |
| 109 | |
| 110 | ## 常見問題 |
| 111 | |
| 112 | ### Q1: 何時該用 AutoFixture,何時該用固定值? |
| 113 | |
| 114 | - **用 AutoFixture**:不影響測試結果的「填充」資料,如使用者的 Email、地址等 |
| 115 | - **用固定值**:影響測試邏輯的「關鍵」資料,如折扣計算中的客戶類型、年齡驗證中的年齡值 |
| 116 | |
| 117 | ### Q2: CreateMany 預設產生幾個?如何調整? |
| 118 | |
| 119 | 預設產生 3 個元素,可透過參數指定數量:`fixture.CreateMany<T>(10)`。若要全域調整,使用 `fixture.RepeatCount = 5`。 |
| 120 | |
| 121 | ### Q3: AutoFixture 產生的字串格式不符合需求? |
| 122 | |
| 123 | 使用 `Build<T>().With()` 指定格式,或搭配 `Customize<T>()` 設定全域規則。若需要真實感資料,考慮整合 Bogus。 |
| 124 | |
| 125 | ## 最佳實踐 |
| 126 | |
| 127 | ### 應該做 |
| 128 | |
| 129 | 1. **使用匿名測試概念** — 專注於測試邏輯而非具體資料 |
| 130 | 2. **只在必要時固定特定值** — 使用 `Build<T>().With()` 設定關鍵屬性 |
| 131 | 3. **建立共用基底類別** — 統一處理循環參考等共同配置 |
| 132 | 4. **合理的集合大小** — 根據測試目的調整 `CreateMany()` 數量 |
| 133 | |
| 134 | ### 應該避免 |
| 135 | |
| 136 | 1. **過度依賴隨機值** — 不要假設隨機值的具體內容 |
| 137 | 2. **忽略邊界值** — 仍需要明確測試邊界情況 |
| 138 | 3. **濫用自動產生** — 簡單測試可能用固定值更清楚 |
| 139 | |
| 140 | ## 程式碼範本 |
| 141 | |
| 142 | 請參考 [templates](./templates) 資料夾中的範例檔案: |
| 143 | |
| 144 | - [basic-autofixture-usage.cs](./templates/basic-autofixture-usage.cs) - 基本使用方式 |
| 145 | - [complex-object-scenarios.cs](./templates/complex-obj |