Len.StronglyTypedId.Testing.AutoFixture 1.4.1

dotnet add package Len.StronglyTypedId.Testing.AutoFixture --version 1.4.1
                    
NuGet\Install-Package Len.StronglyTypedId.Testing.AutoFixture -Version 1.4.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Len.StronglyTypedId.Testing.AutoFixture" Version="1.4.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Len.StronglyTypedId.Testing.AutoFixture" Version="1.4.1" />
                    
Directory.Packages.props
<PackageReference Include="Len.StronglyTypedId.Testing.AutoFixture" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Len.StronglyTypedId.Testing.AutoFixture --version 1.4.1
                    
#r "nuget: Len.StronglyTypedId.Testing.AutoFixture, 1.4.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Len.StronglyTypedId.Testing.AutoFixture@1.4.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Len.StronglyTypedId.Testing.AutoFixture&version=1.4.1
                    
Install as a Cake Addin
#tool nuget:?package=Len.StronglyTypedId.Testing.AutoFixture&version=1.4.1
                    
Install as a Cake Tool

Len.StronglyTypedId.Testing

Test helpers for Len.StronglyTypedId: create strongly typed ids with plausible values instead of hand-writing OrderId.Create(Guid.NewGuid()) in every arrange block.

Three packages:

Package Purpose Dependencies
Len.StronglyTypedId.Testing Any / AnyIdGenerator: value generation for every supported primitive none
Len.StronglyTypedId.Testing.AutoFixture fixture.Create<OrderId>() support AutoFixture
Len.StronglyTypedId.Testing.Bogus faker.Generate<Order>() support Bogus

Quick start

using Len.StronglyTypedId.Testing;

OrderId orderId = Any.Id<OrderId>();
IReadOnlyList<OrderId> batch = Any.Ids<OrderId>(10);

The generator reads the wrapped primitive from the implemented IStronglyTypedId<TSelf, TPrimitiveId> interface, so it works for ids declared in any assembly, including ones generated after this package was compiled. Every primitive the generator supports is covered, plus enum.

Reproducible values

// Same seed ⇒ same value on any .NET version and platform.
OrderId first = Any.Id<OrderId>(seed: 42);
OrderId again = Any.Id<OrderId>(seed: 42);   // equal to first

// A reproducible sequence needs one generator, not one call per id.
IReadOnlyList<OrderId> ids = Any.Ids<OrderId>(10, seed: 42);

// Or keep the generator around.
var any = new AnyIdGenerator(new AnyIdOptions { Seed = 42, StringLength = 12 });
CustomerId customerId = any.Id<CustomerId>();

Reproducibility is guaranteed across .NET versions and platforms: the value source is a built-in SplitMix64 rather than Random, whose algorithm is an implementation detail that may change between releases. Golden-file and snapshot tests can therefore assert on exact values. Without a seed the generator is seeded from the clock, as before.

Ids with a validator

A validator is an arbitrary predicate, so no generator can guarantee a satisfying value. The generator retries with fresh values up to AnyIdOptions.MaxAttempts (100 by default) and then fails with an InvalidOperationException telling you to register a factory:

Any.Register<InvoiceNumber>(() => InvoiceNumber.Create(NextInvoiceNumber()));

A rejection is recognised by its type: ArgumentException by default, or whatever the id configures through ExceptionType (per id, falling back to [assembly: StronglyTypedIdDefaults]). Anything else thrown by the validator is a bug in the validator, so it propagates instead of being retried.

The exception type is the only signal, so a validator that throws ArgumentException for an unrelated reason (a null argument, a failed cast, an invariant it asserts) is indistinguishable from a rejection: the value is retried until MaxAttempts and then reported as "cannot generate". Keep validators total (return false instead of throwing), or register a factory for that id.

Register affects the shared generator used by the static Any members; call Any.Reset() between tests to drop registrations. Prefer a dedicated AnyIdGenerator instance when a test needs isolation.

Custom primitive values

var any = new AnyIdGenerator(new AnyIdOptions
{
    PrimitiveValueFactory = type => type == typeof(Guid) ? KnownGuid : null,
});

Return null to fall back to the built-in generator for that type.

Boundary values

Values stay ordinary by default: int.MaxValue is reachable in principle but never actually drawn. Turn on IncludeBoundaryValues when the code under test is boundary-sensitive (formatting width, overflow checks, database column limits):

var any = new AnyIdGenerator(new AnyIdOptions { IncludeBoundaryValues = true });

Roughly one value in sixteen is then the primitive's MinValue or MaxValue (integral and decimal types only). Leave it off when a test asserts uniqueness, because two ids can then collide on the same extreme.

AutoFixture

using AutoFixture;
using Len.StronglyTypedId.Testing;

var fixture = new Fixture().WithStronglyTypedIds();

OrderId orderId = fixture.Create<OrderId>();

The customization is inserted at the front of the customization list, so it wins over AutoFixture's constructor-based strategy, which would otherwise bypass Create and therefore the validator. Pass a seeded AnyIdGenerator to WithStronglyTypedIds for reproducible fixtures.

Bogus

using Bogus;
using Len.StronglyTypedId.Testing;

var faker = new Faker<Order>().WithStronglyTypedIds();

Order order = faker.Generate();   // order.Id is a real id

Bogus fills a member by its declared type, and a strongly typed id is a distinct type, so without this adapter Bogus cannot produce one and the member stays at its default value without any error. The adapter registers Bogus' own RuleForType rules for every id type, so generation fills them like any other member.

Bogus instantiates the target type through its parameterless constructor, so the type it fills needs one: a class with settable properties, not a record with a primary constructor.

The id types are discovered from the assemblies loaded right now. When the assembly declaring an id is not loaded yet, name the types explicitly:

var faker = new Faker<Order>()
    .WithStronglyTypedIds(new AnyIdGenerator(), typeof(OrderId), typeof(CustomerId));

Pass a seeded AnyIdGenerator for reproducible data.

Bogus applies rules in registration order, so the last one wins. Call WithStronglyTypedIds before any RuleFor you write for an id property, or your rule is the one that gets overridden.

Notes

  • Values are deliberately ordinary: finite floating-point numbers (either sign), non-empty strings, dates inside 1970–2100. A fixture handing out double.NaN or DateTime.MaxValue mostly produces failures unrelated to the code under test; NaN is not even valid JSON, so a round-trip test would fail for a reason that has nothing to do with the id.
  • Strings are drawn from letters, digits and the separators @ . - _ / :, so a validator that looks for an @ or a . can be satisfied without registering a factory. Anything stricter (a real email regex) still needs Register.
  • AnyIdGenerator is thread-safe, but the sequence it produces under parallel access is not deterministic even with a fixed seed.
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.4.1 0 11/6/2026
1.4.0 96 10/30/2026