Conventions cheat-sheet
The short version of the project structure and vocabulary guides. Distilled from the example’s three convention docs.
The cardinal rule
Section titled “The cardinal rule”Arrange and Act produce artifacts. Inspect only observes — reads OK, mutations not.
- If an inspect performs the thing under test → lift it into Act and pass the artifact on.
- If an act asserts → move the assertion into Inspect; Act should just return/capture the artifact.
Test bodies
Section titled “Test bodies”- No raw setup or assertions in a test body. Every value is a business-named
Arrange; every check a business-namedInspect. - Three blocks, in order:
await Arrange…→await Act…→await Inspect…, nothing else interleaved. - The what lives in the test body; the how lives in reusable verbs.
Vocabulary
Section titled “Vocabulary”- Verbs are extension methods on
ITestArrange/ITestAct/ITestInspect, returning the same, ending in.Then(host => …). - Colocate per feature/SUT:
Arrange<Feature>.cs·Act<Feature>.cs·Inspect<Feature>.csnext to the tests, same namespace. - Cross-feature verbs live in a root-namespace
Helpers/folder (in scope everywhere). - Defaults at the top, variation via mutators — build from one default +
params …Fn[]; tests list only what differs (WithName,WithEmail, …). - Set up mocks inside
host.Execute<Mock<T>>(…); seed the DB insidehost.ExecuteAsync<Context>(…). Await async work — return theTaskfrom.Then(async host => await …).
Captures
Section titled “Captures”- Capture, don’t return. Surface values built in Arrange via an
out Capture<T>/out Trapture<T>. - Captures fill when the chain is awaited — don’t read
.Valuewhile building a later step in the same chain;awaitthe producer first. Trapture<T>converts implicitly (transparent flow);Capture<T>needs.Value(explicit read).- Use
Ensureto derive-guard-capture an id in one step.
Stage & fixtures
Section titled “Stage & fixtures”- Compose containers once (
TestStageSetup.Create);EnterStage()per test; dispose after. - One fixture per SUT — a type can’t be both the real SUT and a mock in one composition.
- Base fixture exposes
Arrange/Act/Inspectproperties over the current stage.
Assertions & effects
Section titled “Assertions & effects”- Result assertions → a value scope (
ThenValueScope); a context scope wraps them (e.g.Assert.Multiple). - State assertions read the real DB/API; interaction assertions verify mocks (
Received,Verify). - Async effects → poll (
ApiClientEventually, a probe). NeverTask.Delaya fixed guess. - Snapshot whole objects with
Verify(capture)— keep values deterministic (Clock/Ids) so snapshots are stable.
Determinism
Section titled “Determinism”- Depend on
IDateTimeProvider/IIdGenerator, notDateTime.UtcNow/Guid.NewGuid(). - Arrange them with
Clock(…)/Ids(…)and shared fixed constants.