SuperAPI AI Directory

dotnet-testing-autofixture-customization

Testing & quality

View repository

Updated 10-15 23:45

About this skill

AutoFixture Advanced: Customized Test Data Generation Strategies

Overview

This skill covers AutoFixture's advanced customization features, allowing you to precisely control test data generation logic according to business requirements.

Core Techniques

  1. DataAnnotations Integration: AutoFixture automatically recognizes [StringLength]、[Range] and other validation attributes
  2. Attribute Range Control: Use .With() with Random.Shared to dynamically generate random values
  3. Custom ISpecimenBuilder: Implement builders that precisely control specific properties
  4. Priority Management: Understand the difference between Insert(0) vs Add() difference
  5. 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

  1. Make good use of DataAnnotations — fully leverage existing model validation rules; AutoFixture automatically generates data that meets the constraints
  2. Use Random.Shared — avoid duplicate value issues, thread-safe, better performance
  3. Insert(0) ensures priority — custom numeric builders must use Insert(0)
  4. Generic Design — create reusable generic builders, use extension methods to provide a fluent interface

Should Avoid

  1. Ignoring builder priority — do not assume Add() will definitely take effect
  2. overly complex logic — the builder maintains a single responsibility
  3. Use new Random() — may produce duplicate values, not thread-safe

Code template

Please refer to templates sample files in the folder:

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:

Official documentation