dotnet-testing-autofixture-bogus-integration

AutoFixture 與 Bogus 整合完整指南。當需要結合 AutoFixture 與 Bogus 產生兼具匿名性與真實感的測試資料時使用。涵蓋 SpecimenBuilder 整合、混合產生器、測試資料工廠與循環參考處理。 Make sure to use this skill whenever the user mentions AutoFixture with Bogus, Faker integration, realistic test data, semantic test data, or HybridTestDataGenerator, even if they don't explicitly ask for integration guidance. Keywords: autofixture bogus integration, autofixture bogus, bogus integration, Faker, EmailSpecimenBuilder, PhoneSpecimenBuilder, NameSpecimenBuilder, 真實感測試資料, 語意化資料, 混合產生器, HybridTestDataGenerator, OmitOnRecursionBehavior, 循環參考

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "dotnet-testing-autofixture-bogus-integration" with this command: npx skills add kevintsengtw/dotnet-testing-agent-skills/kevintsengtw-dotnet-testing-agent-skills-dotnet-testing-autofixture-bogus-integration

AutoFixture 與 Bogus 整合應用指南

核心概念

為什麼需要整合?

AutoFixture 的優勢

  • 快速產生匿名測試資料
  • 自動處理複雜物件結構
  • 良好的循環參考處理機制

Bogus 的優勢

  • 產生真實感的語意化資料
  • 豐富的資料類型支援(Email、Phone、Address 等)
  • 對驗證比較友善的資料格式

整合後的效果

// 整合前的問題
var user = fixture.Create<User>();
// user.Email 可能是 "Email1a2b3c4d",不像真實 Email

// 整合後
var user = integratedFixture.Create<User>();
// user.Email 是 "john.doe@example.com"
// user.FirstName 是 "John"
// 其他屬性由 AutoFixture 自動填充

套件安裝

<PackageReference Include="AutoFixture" Version="4.18.1" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.18.1" />
<PackageReference Include="Bogus" Version="35.6.3" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="AwesomeAssertions" Version="9.1.0" />

整合架構

整合方式總覽

整合方式適用場景複雜度
屬性層級 SpecimenBuilder特定屬性使用 Bogus
類型層級 SpecimenBuilder整個類型使用 Bogus
混合產生器 (HybridGenerator)統一 API 整合
整合工廠 (IntegratedFactory)完整測試場景建構
自訂 AutoData 屬性xUnit 整合

核心整合技術

透過 ISpecimenBuilder 介面實現屬性層級與類型層級的整合,搭配擴充方法(WithBogus()WithOmitOnRecursion()WithSeed())簡化設定流程。涵蓋 Email、Phone、Name、Address 等常用 SpecimenBuilder 以及完整的類型產生器註冊模式。

完整內容請參閱 references/core-integration-techniques.md


循環參考處理

為什麼循環參考很重要?

public class User
{
    public Company? Company { get; set; }  // User 參考 Company
}

public class Company
{
    public List<User> Employees { get; set; } = new();  // Company 參考 User
}

問題:User → Company → Employees(User) → Company → ... 無限循環

解決方案:OmitOnRecursionBehavior

var fixture = new Fixture();
fixture.Behaviors.OfType<ThrowingRecursionBehavior>()
    .ToList()
    .ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior());

效果

  • 避免 StackOverflowException
  • 循環參考的屬性設為 null 或空集合
  • 某些深層屬性可能為 null(這是預期行為)

自訂 AutoData 屬性

BogusAutoDataAttribute

public class BogusAutoDataAttribute : AutoDataAttribute
{
    public BogusAutoDataAttribute()
        : base(() => new Fixture().WithBogus())
    {
    }
}

使用方式

[Theory]
[BogusAutoData]
public void 使用整合資料測試(User user, Address address)
{
    user.Email.Should().Contain("@");
    user.FirstName.Should().NotBeNullOrEmpty();
    address.City.Should().NotBeNullOrEmpty();
}

混合產生器

ITestDataGenerator 介面

public interface ITestDataGenerator
{
    T Generate<T>();
    IEnumerable<T> Generate<T>(int count);
    T Generate<T>(Action<T> configure);
}

HybridTestDataGenerator 實作

public class HybridTestDataGenerator : ITestDataGenerator
{
    private readonly IFixture _fixture;

    public HybridTestDataGenerator(int? seed = null)
    {
        _fixture = new Fixture()
            .WithBogus()
            .WithOmitOnRecursion();

        if (seed.HasValue)
        {
            Bogus.Randomizer.Seed = new Random(seed.Value);
        }
    }

    public T Generate<T>() => _fixture.Create<T>();

    public IEnumerable<T> Generate<T>(int count)
        => Enumerable.Range(0, count).Select(_ => Generate<T>());

    public T Generate<T>(Action<T> configure)
    {
        var item = Generate<T>();
        configure(item);
        return item;
    }
}

整合測試資料工廠

IntegratedTestDataFactory

public class IntegratedTestDataFactory
{
    private readonly IFixture _fixture;
    private readonly Dictionary<Type, object> _cache = new();

    public IntegratedTestDataFactory(int? seed = null)
    {
        _fixture = new Fixture()
            .WithBogus()
            .WithOmitOnRecursion()
            .WithRepeatCount(3);

        if (seed.HasValue)
        {
            _fixture.WithSeed(seed.Value);
        }
    }

    public T CreateFresh<T>() => _fixture.Create<T>();

    public List<T> CreateMany<T>(int count = 3)
        => _fixture.CreateMany<T>(count).ToList();

    public T GetCached<T>() where T : class
    {
        var type = typeof(T);
        if (_cache.TryGetValue(type, out var cached))
            return (T)cached;

        var instance = CreateFresh<T>();
        _cache[type] = instance;
        return instance;
    }

    public void ClearCache() => _cache.Clear();

    /// <summary>
    /// 建立完整測試場景
    /// </summary>
    public TestScenario CreateTestScenario()
    {
        var company = CreateFresh<Company>();
        var users = CreateMany<User>(5);
        var orders = CreateMany<Order>(10);

        // 建立關聯
        foreach (var user in users)
        {
            user.Company = company;
        }

        company.Employees = users;

        return new TestScenario
        {
            Company = company,
            Users = users,
            Orders = orders
        };
    }
}

測試基底類別

TestBase 實作

public abstract class TestBase
{
    protected readonly IFixture Fixture;
    protected readonly HybridTestDataGenerator Generator;
    protected readonly IntegratedTestDataFactory Factory;

    protected TestBase(int? seed = null)
    {
        Fixture = new Fixture()
            .WithBogus()
            .WithOmitOnRecursion()
            .WithRepeatCount(3);

        if (seed.HasValue)
        {
            Fixture.WithSeed(seed.Value);
        }

        Generator = new HybridTestDataGenerator(seed);
        Factory = new IntegratedTestDataFactory(seed);
    }

    protected T Create<T>() => Fixture.Create<T>();

    protected List<T> CreateMany<T>(int count = 3)
        => Fixture.CreateMany<T>(count).ToList();

    protected T Create<T>(Action<T> configure)
    {
        var instance = Create<T>();
        configure(instance);
        return instance;
    }
}

Seed 管理與可重現性

重要限制

由於 AutoFixture 和 Bogus 有不同的隨機數管理機制:

  • Seed 確保測試行為穩定性
  • Seed 確保資料格式一致性
  • 無法保證所有屬性值完全相同

建議做法

// 使用 Seed 確保穩定性
var factory = new IntegratedTestDataFactory(seed: 12345);

// 如果需要完全可重現,使用單一工具
var faker = new Faker<User>();
faker.UseSeed(12345);

使用範例

基本整合使用

[Fact]
public void AutoFixture_整合_Bogus_應能產生真實感資料()
{
    // Arrange
    var fixture = new Fixture().WithBogus();

    // Act
    var user = fixture.Create<User>();

    // Assert
    user.Email.Should().Contain("@");
    user.FirstName.Should().NotBeNullOrEmpty();
    user.Phone.Should().MatchRegex(@"[\d\-\(\)\s]+");
}

使用工廠建立測試場景

[Fact]
public void 工廠_應能建立完整的測試場景()
{
    // Arrange
    var factory = new IntegratedTestDataFactory(seed: 42);

    // Act
    var scenario = factory.CreateTestScenario();

    // Assert
    scenario.Company.Should().NotBeNull();
    scenario.Users.Should().HaveCount(5);
    scenario.Orders.Should().HaveCount(10);

    scenario.Users.Should().AllSatisfy(user =>
    {
        user.Company.Should().Be(scenario.Company);
        user.Email.Should().Contain("@");
    });
}

最佳實踐

建議做法

  1. 永遠先處理循環參考

    fixture.WithOmitOnRecursion().WithBogus();
    
  2. 為常用實體建立專用 SpecimenBuilder

  3. 使用 Seed 確保測試穩定性

  4. 建立測試基底類別統一資料產生邏輯

  5. 適當使用快取提升效能

避免事項

  1. 過度設計,保持簡單實用
  2. 期望整合環境完全可重現
  3. 忽略循環參考處理
  4. 在每個測試中重新建立 Fixture

整合與 AutoFixture/Bogus 的比較

面向純 AutoFixture純 Bogus整合方案
資料真實感
設定複雜度
物件關聯處理自動手動自動
循環參考處理內建整合
可重現性
適用場景單元測試整合測試/原型兩者皆可

輸出格式

  • 產生 ISpecimenBuilder 實作類別檔案(如 EmailSpecimenBuilder.cs
  • 產生 Fixture 擴充方法類別檔案(FixtureBogusExtensions.cs
  • 產生整合測試資料工廠或混合產生器類別
  • 產生 BogusAutoDataAttribute 供 xUnit Theory 測試使用
  • 搭配 OmitOnRecursionBehavior 處理循環參考

參考資源

原始文章

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

官方文件


相關技能

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

dotnet-testing-advanced-webapi-integration-testing

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

dotnet-testing-unit-test-fundamentals

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

dotnet-testing-xunit-project-setup

No summary provided by upstream source.

Repository SourceNeeds Review