Specimen builders and specifications

How ISpecimenBuilder nodes and IRequestSpecification filters compose into AutoFixture's kernel graph.

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 also ParameterInfo, PropertyInfo, or custom objects.
  • ContextISpecimenContext.Resolve creates dependent specimens (constructor arguments, property values, nested types).
  • Result — a specimen, or NoSpecimen.Instance to 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:

SpecificationMatches
ExactTypeSpecificationA Type request equal to a target type
ParameterSpecificationA constructor parameter by type and name
PropertySpecificationA writable property by type and name
AbstractTypeSpecificationInterface or abstract class Type requests
AndRequestSpecificationAll 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:

NodeRole
FilteringSpecimenBuilderRuns an inner builder only when a specification matches
CompositeSpecimenBuilderTries child builders in order
TypeRelayMaps one Type request to another (uses ExactTypeSpecification internally)
FixedBuilderAlways 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 a Type request; reflection triggers subsidiary requests for parameters and properties.
  • FilteringSpecimenBuilder returns NoSpecimen.Instance when the specification fails — the composite moves to the next sibling.
  • TypeRelay does not construct directly; it calls context.Resolve for 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.

Next steps

API