Overview
This skill covers AutoFixture's advanced customization features, allowing you to precisely control test data generation logic according to business requirements.
Core Techniques
- DataAnnotations Integration: AutoFixture automatically recognizes
[StringLength]、[Range]and other validation attributes - Attribute Range Control: Use
.With()withRandom.Sharedto dynamically generate random values - Custom ISpecimenBuilder: Implement builders that precisely control specific properties
- Priority Management: Understand the difference between
Insert(0)vsAdd()difference - Generic Design: Create reusable builders that support multiple numeric types
Install Packages
<PackageReference Include="AutoFixture" Version="4.18.1" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.18.1" />
DataAnnotations Automatic Integration
AutoFixture can automatically recognize System.ComponentModel.DataAnnotations validation attributes:
public class Person
{
public Guid Id { get; set; }
[StringLength(10)]
public string Name { get; set; } = string.Empty;
[Range(10, 80)]
public int Age { get; set; }
public DateTime CreateTime { get; set; }
}
[Fact]
public void AutoFixture_應能識別DataAnnotations()
{
var fixture = new Fixture();
var person = fixture.Create<Person>();
person.Name.Length.Should().Be(10); // StringLength(10)
person.Age.Should().BeInRange(10, 80); // Range(10, 80)
}
Use .With() to control attribute ranges
Fixed Values vs Dynamic Values
// ❌ 固定值:只執行一次,所有物件相同值
.With(x => x.Age, Random.Shared.Next(30, 50))
// ✅ 動態值:每個物件都重新計算
.With(x => x.Age, () => Random.Shared.Next(30, 50))
Advantages of Random.Shared
| Feature | new Random() |
Random.Shared |
|---|---|---|
| Instantiation Method | Creates a new instance each time | Globally shared single instance |
| Thread Safety | No | Yes |
| Performance | Repeated creation has overhead, may produce duplicate values | Better performance, avoids duplicate values |
Custom ISpecimenBuilder
Covers RandomRangedDateTimeBuilder (precise control of specific DateTime properties), ImprovedRandomRangedNumericSequenceBuilder (improved numeric range builder), generic NumericRangeBuilder<TValue> (supports multiple types such as int/long/decimal/double/float), and the fluent interface extension method AddRandomRange. Each builder comes with a complete implementation and usage examples.
For complete ISpecimenBuilder implementation examples, refer to references/specimen-builder-examples.md
Priority Management: Insert(0) vs Add()
AutoFixture's built-in RangeAttributeRelay、NumericSequenceGenerator may have higher priority than custom builders:
// ❌ 可能失效:被內建建構器攔截
fixture.Customizations.Add(new MyNumericBuilder(30, 50, "Age"));
// ✅ 正確:確保最高優先順序
fixture.Customizations.Insert(0, new MyNumericBuilder(30, 50, "Age"));
int vs DateTime Handling Differences
| Type | Built-in Builder | Priority Impact |
|---|---|---|
int |
RangeAttributeRelay、NumericSequenceGenerator |
Will be intercepted, need to use Insert(0) |
DateTime |
No specific builder | Will not be intercepted,Add() is sufficient |
Best Practices
Should Do
- Make good use of DataAnnotations — fully leverage existing model validation rules; AutoFixture automatically generates data that meets the constraints
- Use Random.Shared — avoid duplicate value issues, thread-safe, better performance
- Insert(0) ensures priority — custom numeric builders must use
Insert(0) - Generic Design — create reusable generic builders, use extension methods to provide a fluent interface
Should Avoid
- Ignoring builder priority — do not assume
Add()will definitely take effect - overly complex logic — the builder maintains a single responsibility
- Use new Random() — may produce duplicate values, not thread-safe
Code template
Please refer to templates sample files in the folder:
- dataannotations-integration.cs - Automatic DataAnnotations integration
- custom-specimen-builders.cs - Custom ISpecimenBuilder implementation
- numeric-range-extensions.cs - Genericized numeric range builder and extension methods
Relationship with other skills
- autofixture-basics: Prerequisite knowledge for this skill; basic usage must be mastered first
- autodata-xunit-integration: Next learning objective; integrate customization with xUnit
- autofixture-nsubstitute-integration: Advanced integration; combine Mock with custom data generation
Output format
- Generate a custom ISpecimenBuilder implementation class
- Generate an ICustomization composition class
- Provide a fixture.Customizations.Insert(0, ...) configuration example
- Include DataAnnotations integration and genericized builder code
Reference resources
Original article
The content of this skill is distilled from the "Old-School Software Engineer's Testing Training - 30-Day Challenge" series:
- Day 11 - AutoFixture Advanced: Customized Test Data Generation Strategies
- Ironman series article:https://ithelp.ithome.com.tw/articles/10375153
- Sample code:https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day11