xiaohongshu
xiaohongshu
dotnet-testing-autofixture-bogus-integration
AutoFixture 與 Bogus 整合完整指南。結合匿名測試資料與真實感語意化資料產生。涵蓋 SpecimenBuilder 整合、混合產生器、測試資料工廠與循環參考處理。 Keywords: autofixture bogus integration, autofixture bogus, bogus integration, Faker, EmailSpecimenBuilder, PhoneSpecimenBuilder, NameSpecimenBuilder, 真實感測試資料, 語意化資料, 混合產生器, HybridTestDataGenerator, OmitOnRe...
Full skill instructions
當被要求執行以下任務時,請使用此技能:
AutoFixture 的優勢:
Bogus 的優勢:
整合後的效果:
// 整合前的問題
var user = fixture.Create<User>();
// user.Email 可能是 "Email1a2b3c4d",不像真實 Email
// 整合後
var user = integratedFixture.Create<User>();
// user.Email 是 "[email protected]"
// 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 介面,根據屬性名稱決定是否使用 Bogus:
public class EmailSpecimenBuilder : ISpecimenBuilder
{
private readonly Faker _faker = new();
public object Create(object request, ISpecimenContext context)
{
if (request is PropertyInfo property &&
property.Name.Contains("Email", StringComparison.OrdinalIgnoreCase))
{
return _faker.Internet.Email();
}
return new NoSpecimen();
}
}
常用 SpecimenBuilder:
| Builder | 匹配條件 | Bogus 方法 |
|---|---|---|
| EmailSpecimenBuilder | 包含 "Email" | Internet.Email() |
| PhoneSpecimenBuilder | 包含 "Phone" | Phone.PhoneNumber() |
| NameSpecimenBuilder | FirstName/LastName/FullName | Person.FirstName/LastName/FullName |
| AddressSpecimenBuilder | Street/City/Postal/Country | Address.* |
| WebsiteSpecimenBuilder | 包含 "Website" | Internet.Url() |
| CompanyNameSpecimenBuilder | Company 類型的 Name | Company.CompanyName() |
為整個類型建立完整的 Bogus 產生器:
public class BogusSpecimenBuilder : ISpecimenBuilder
{
private readonly Dictionary<Type, object> _fakers;
public BogusSpecimenBuilder()
{
_fakers = new Dictionary<Type, object>();
RegisterFakers();
}
private void RegisterFakers()
{
_fakers[typeof(User)] = new Faker<User>()
.RuleFor(u => u.Id, f => f.Random.Guid())
.RuleFor(u => u.FirstName, f => f.Person.FirstName)
.RuleFor(u => u.Email, (f, u) => f.Internet.Email(u.FirstName))
.Ignore(u => u.Company); // 避免循環參考
_fakers[typeof(Address)] = new Faker<Address>()
.RuleFor(a => a.Street, f => f.Address.StreetAddress())
.RuleFor(a => a.City, f => f.Address.City());
}
public object Create(object request, ISpecimenContext context)
{
if (request is Type type && _fakers.TryGetValue(type, out var faker))
{
return GenerateWithFaker(faker);
}
return new NoSpecimen();
}
}
public static class FixtureExtensions
{
/// <summary>
/// 為 AutoFixture 加入 Bogus 整合功能
/// </summary>
public static IFixture WithBogus(this IFixture fixture)
{
// 先處理循環參考
fixture.WithOmitOnRecursion();
// 加入屬性層級整合
fixture.Customizations.Add(new EmailSpecimenBuilder());
fixture.Customizations.Add(new PhoneSpecimenBuilder());
fixture.Customizations.Add(new NameSpecimenBuilder());
fixture.Customizations.Add(new AddressSpecimenBuilder());
// 加入類型層級整合
fixture.Customizations.Add(new BogusSpecimenBuilder());
return fixture;
}
/// <summary>
/// 處理循環參考
/// </summary>
public static IFixture WithOmitOnRecursion(this IFixture fixture)
{
fixture.Behaviors.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior());
return fixture;
}
/// <summary>
/// 設定隨機種子
/// </summary>
public static IFixture WithSeed(this IFixture fixture, int seed)
{
Bogus.Randomizer.Seed = new Random(seed);
return fixture;
}
}
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 → ... 無限循環
var fixture = new Fixture();
fixture.Behaviors.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior());
效果:
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();
}
public interface ITestDataGenerator
{
T Generate<T>();
IEnumerable<T> Generate<T>(int count);
T Generate<T>(Action<T> configure);
}
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;
}
}
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
};
}
}
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;
}
}
由於 AutoFixture 和 Bogus 有不同的隨機數管理機制:
// 使用 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("@");
});
}
永遠先處理循環參考
fixture.WithOmitOnRecursion().WithBogus();
為常用實體建立專用 SpecimenBuilder
使用 Seed 確保測試穩定性
建立測試基底類別統一資料產生邏輯
適當使用快取提升效能
| 面向 | 純 AutoFixture | 純 Bogus | 整合方案 |
|---|---|---|---|
| 資料真實感 | 低 | 高 | 高 |
| 設定複雜度 | 低 | 中 | 中 |
| 物件關聯處理 | 自動 | 手動 | 自動 |
| 循環參考處理 | 內建 | 無 | 整合 |
| 可重現性 | 高 | 高 | 中 |
| 適用場景 | 單元測試 | 整合測試/原型 | 兩者皆可 |
本技能內容提煉自「老派軟體工程師的測試修練 - 30 天挑戰」系列文章:
請參考同目錄下的範例檔案:
templates/specimen-builders.cs - SpecimenBuilder 實作範例templates/hybrid-generator.cs - 混合產生器與擴充方法templates/integrated-factory.cs - 整合工廠與測試基底類別xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience