Specimen builders and specifications
AutoFixture resolves every Create call through a graph of specimen builders. Each node answers one question: given this request, can I produce a specimen?
This article is about that kernel graph — builders, specifications, and how they connect. It is not about nested object graphs produced by Create<T>(); see Specimen graphs for that.
What is a specimen builder?
A specimen builder implements ISpecimenBuilder:
public interface ISpecimenBuilder
{
object Create(object request, ISpecimenContext context);
}
- Request — what AutoFixture is trying to create. Often a
Type(typeof(ClientDto)), but alsoParameterInfo,PropertyInfo, or custom objects. - Context —
ISpecimenContext.Resolvecreates dependent specimens (constructor arguments, property values, nested types). - Result — a specimen, or
NoSpecimen.Instanceto decline and let the next builder try.
Builders form a chain of responsibility. CompositeSpecimenBuilder runs children in order until one returns a specimen. See Specimen pipeline for where customizations, the engine, and residue collectors sit in the full layout.
The specification pattern
A request specification (IRequestSpecification) decides whether a request matches a rule:
public interface IRequestSpecification
{
bool IsSatisfiedBy(object request);
}
This is the Specification pattern applied to specimen requests, not to domain objects. Specifications answer: should this builder handle this request?
AutoFixture ships many built-in specifications, including:
| Specification | Matches |
|---|---|
ExactTypeSpecification | A Type request equal to a target type |
ParameterSpecification | A constructor parameter by type and name |
PropertySpecification | A writable property by type and name |
AbstractTypeSpecification | Interface or abstract class Type requests |
AndRequestSpecification | All of several specifications |
You can implement IRequestSpecification when built-in filters are not enough.
Specifications plus builders as a graph
Most kernel nodes combine filter + action:
| Node | Role |
|---|---|
FilteringSpecimenBuilder | Runs an inner builder only when a specification matches |
CompositeSpecimenBuilder | Tries child builders in order |
TypeRelay | Maps one Type request to another (uses ExactTypeSpecification internally) |
FixedBuilder | Always returns the same specimen |
Fixture wires these into a deep graph at startup — CompositeSpecimenBuilder chains for dictionaries and lists, TypeRelay nodes in residue collectors for IList<>, FilteringSpecimenBuilder nodes for invariant culture, and more. Your Customizations.Add calls append nodes to the front of that graph.
Think of it as a graph of specification-gated builders: each edge is if specification matches, run this builder (which may call context.Resolve and trigger more of the graph).
Example scenario
You are testing PricingService, which depends on IOrderRepository and a currency string constructor parameter. You want a concrete in-memory repository and a fixed currency code without hand-writing the service in every test.
Example
Built-in relay — map interface Type requests to a concrete class:
var fixture = new Fixture();
fixture.Customizations.Add(
new TypeRelay(typeof(IOrderRepository), typeof(InMemoryOrderRepository)));
Built-in specification + builder — pin the currency parameter:
fixture.Customizations.Add(
new FilteringSpecimenBuilder(
new FixedBuilder("EUR"),
new ParameterSpecification(typeof(string), "currency")));
Custom specification — only handle decimal property requests named MinimumOrderAmount:
fixture.Customizations.Add(
new FilteringSpecimenBuilder(
new FixedBuilder(25m),
new MinimumOrderAmountPropertySpecification()));
var service = fixture.Create<PricingService>();
Assert.IsType<InMemoryOrderRepository>(service.Repository);
Assert.Equal("EUR", service.Currency);
Assert.Equal(25m, service.MinimumOrderAmount);
public sealed class MinimumOrderAmountPropertySpecification : IRequestSpecification
{
public bool IsSatisfiedBy(object request)
{
return request is PropertyInfo property
&& property.PropertyType == typeof(decimal)
&& property.Name == nameof(PricingService.MinimumOrderAmount);
}
}
TypeRelay and FilteringSpecimenBuilder are composed with built-in specifications. MinimumOrderAmountPropertySpecification is custom, but plugs into the same graph shape.
How it works
- Every
Create<T>()enters the graph with aTyperequest; reflection triggers subsidiary requests for parameters and properties. FilteringSpecimenBuilderreturnsNoSpecimen.Instancewhen the specification fails — the composite moves to the next sibling.TypeRelaydoes not construct directly; it callscontext.Resolvefor the target type, re-entering the graph.- Customizations prepend nodes; residue collectors append fallbacks after the engine. Same builder types, different positions.
Prefer Register, Customize, and Build DSL when they express your rule. Reach for specification-gated builders when you need precise control over which requests in the graph are affected.