Reads like prose
Tests are composed from domain verbs you name yourself — NewClient, CacheHasClient,
EventPublished. The AAA (Arrange / Act / Inspect) shape keeps every test telling the same story.
await Arrange .NewClient(out var client, WithName("Acme Corporation")) .CacheHasClient(client);
var result = await Act(client);
await Inspect .WriteResult(result).Created() .Ensure(result, r => r.ClientId, out var id) .ThenAll( b => b.ApiClientMatches(id, name: "Acme Corporation"), b => b.DbClientExists(id), b => b.EventPublished("clients.created", id));Reads like prose
Tests are composed from domain verbs you name yourself — NewClient, CacheHasClient,
EventPublished. The AAA (Arrange / Act / Inspect) shape keeps every test telling the same story.
No DSL, no feature files
It’s just C#. No Gherkin, no step bindings, no generated glue. Refactor a verb and every test that uses it updates — and still compiles.
Bring your own everything
Framework-agnostic (xUnit / NUnit / MSTest), mock-agnostic (Moq / NSubstitute / FakeItEasy) and container-agnostic (Microsoft DI / Autofac / Castle Windsor — or the dependency-free Bag).
From unit to end-to-end
The same Arrange / Act / Inspect vocabulary scales from a mocked unit test to a full Testcontainers e2e run against real Postgres, Kafka and your API.