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));f.Arrange(). NewClient[Client](WithName("Acme Corporation")). CacheHasClient(f.Of[Client]())
result := f.Act().Create(f.Of[Client]())
f.Inspect(). Created(result). All( apiClientMatches(f, result.ID, "Acme Corporation"), dbClientExists(f, result.ID), eventPublished(f, "clients.created", result.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 your language. No Gherkin, no step bindings, no generated glue. Refactor a verb and every test that uses it updates — and still compiles.
Two languages, one idea
A .NET original and a Go port that share the shape, the vocabulary discipline and the docs. Each speaks its language natively — captures and source generators in C#, tokens and generic methods in Go — and this site teaches both side by side.
Bring your own everything
Framework-, mock- and container-agnostic: xUnit / NUnit / MSTest / TUnit, Moq / NSubstitute / FakeItEasy, Microsoft DI / Autofac — or gomock / mockery / minimock, samber-do / dig — or the dependency-free Bag either side ships.
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.