docs: ADR-0006 test-data builders, close out WP-70

Writes up the principle behind WP-70's three tracks ("build test data
through the same door production code uses") as ADR-0006, with a decision
table for which fixture idiom fits which test type. Updates the
test-strategy skill (adds the Fixtures rule, fixes its stale pre-monorepo
src/app/... worked-example paths) and the shared Storybook testing.mdx page
to match. Closes WP-70 with the signatures/counts as actually shipped.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-08-18 15:31:20 +02:00
co-authored by Claude Sonnet 5
parent a7bf228ca8
commit a82332fa20
5 changed files with 327 additions and 4 deletions
+30 -1
View File
@@ -63,9 +63,38 @@ expect(parseBrpAddress(null).ok).toBe(false);
expect(parseBrpAddress({}).ok).toBe(false); // missing required field
```
Elm-style machines test the pure `reduce` with inline state fixtures — no Angular
Elm-style machines test the pure `reduce` — no Angular
(`registratie/domain/registratie-wizard.machine.spec.ts`).
## Fixtures: build test data through the production door
A fixture is not a shortcut around the domain — it's the domain's own construction path, run
once for the test. [ADR-0006](../../../docs/reference/architecture/0006-test-data-builders.md)
covers this in full (with a backend example too); the frontend idiom is one combinator,
`given` (`libs/shared/src/testing/machine.ts`):
```ts
export const given =
<S, M>(reduce: (s: S, m: M) => S, initial: S) =>
(...msgs: M[]): S =>
msgs.reduce(reduce, initial);
export const givenIntake = given(reduce, initial); // per-context wrapper, pure TS
```
A machine spec replays real `Msg`s instead of hand-writing a `State` literal — so a fixture
can only ever be a state the real reducer actually produces:
```ts
const atStep3 = givenIntake(Start(), SetUren('1200'), Next(), SetDiplomaHerkomst('NL'), Next());
```
The same rule extends to value objects (`unwrapOk(parseX(raw))` instead of a cast) and to
`RemoteData` (`loading()` / `success(v)` / `failure(e)` in
`libs/shared/src/testing/{value-object,remote-data}.ts` instead of a redefined-per-file
literal). **Never** a `.withX().withY()` builder over an open constructor — that just
re-opens whatever illegal state the domain closed.
## UI = Storybook, not heavy component tests
`@storybook/addon-a11y` runs the `wcag2a/2aa/21a/21aa` rule sets on **every** story;