Skip to content

How Do I Use Profiles?

Define one

public sealed class ApplicationTestProfile : ICompositionProfile
{
    public void Configure(CompositionBuilder builder) =>
        builder
            .UseNSubstitute()
            .UseBogus()
            .Register<IClock>(_ => new FakeClock());
}

Apply it programmatically

var composer = Composer.Create(builder => builder.AddProfile<ApplicationTestProfile>());

Apply it to a composed xUnit theory

[Theory]
[Compose<ApplicationTestProfile>]
public void ComposesTheProfileConfiguredValue(NotificationSettings settings) { }

TProfile must implement ICompositionProfile and have a public parameterless constructor — [Compose<TProfile>] enforces this at compile time via a generic constraint.

A profile that needs a value known only at a specific test's call site — not a fixed, default-constructed one — can't use [Compose<TProfile>] at all, since it has no way to receive that value. [Compose<TProfile, TConfig>] covers this: TConfig is a small, strongly-typed configuration object, bound positionally from the attribute's own constructor arguments and passed to TProfile's constructor. See Compono.XunitV3's Package Guide for the full shape and Migrating from AutoFixture for the AutoFixture pattern this replaces.

Combining more than one profile

var composer = Composer.Create(builder => builder
    .AddProfile<DomainProfile>()
    .AddProfile<InfrastructureProfile>());

Profiles apply in the order added. Build up project-wide configuration from a few small, focused profiles rather than one large one — it's easier to reuse a DomainProfile on its own in a test that doesn't need InfrastructureProfile's configuration.

Applying an already-built instance instead of a type

builder.AddProfile(new ApplicationTestProfile());

Use this over AddProfile<TProfile>() only when the profile itself needs constructor arguments — most profiles don't, and AddProfile<TProfile>() is the more common form.

Common mistakes

  • A profile that applies itself again while already applying (directly, or through a nested profile) — this is a cycle, raised immediately as CompositionConfigurationException, not a silently-ignored no-op.
  • Putting per-test assertions or mutable state inside a profile — a profile is pure Composer configuration, applied once, synchronously.

Next