Files
atomic-design-poc/.claude/skills/test-strategy/SKILL.md
T
ehoandClaude Opus 4.8 0edfbba2a9 docs(storybook): render MDX tables + add i18n & testing-strategy docs/skill
- fix: wire remark-gfm into addon-docs so GFM pipe tables in *.mdx render
  (previously raw text in cibg-gaps/layers/atomic-design docs)
- add src/docs/i18n.mdx (Foundations/Internationalization): the $localize
  locale seam + how to test languages without coupling to copy
- add src/docs/testing.mdx (Foundations/Testing strategy): per-layer spec
  matrix, house style, Storybook a11y gate, GREEN gate
- add .claude/skills/test-strategy skill

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 19:26:34 +02:00

2.6 KiB

name, description
name description
test-strategy Place tests the house way — Vitest specs co-located by layer (pure domain, no TestBed; parse* trust boundaries; thin UI via Storybook a11y). Use whenever adding a spec or deciding what to test.

Test strategy (test where it's pure)

Push logic down to where it's pure, test it there directly, keep the layers above thin. No TestBed for domain. Never assert on user-facing copy.

Rules

  • domain/ + any pure logic → required spec. Reducers, combinators, visibleSteps, policies, parsers. Import the function and call it — no Angular, no TestBed.
  • Value-object parser → happy path + normalisation + each rejection. Assert on the Result discriminant (.ok) and the parsed value, not the error message.
  • infrastructure/ parse* (trust boundary) → required spec. Accept a valid DTO; reject null, {}, and malformed shapes. Name it describe('… (trust boundary)').
  • application/ stores/commands → spec the pure reduce + optimistic begin→confirm/rollback + the command Result.
  • ui/ → Storybook story, not a component test. Axe runs on every story; add a play only for wiring axe can't see.
  • Never assert on $localize copy. It changes per locale/edit — assert on the Result, the value object, or the message id.

Skeleton

Co-locate *.spec.ts next to the unit, in the same layer folder:

<context>/domain/<thing>.spec.ts            # pure — no TestBed
<context>/domain/value-objects/<vo>.spec.ts # parser: ok + normalise + each reject
<context>/infrastructure/<x>.adapter.spec.ts# parse* trust boundary
<context>/application/<store|command>.spec.ts

Minimal parser spec:

import { describe, it, expect } from 'vitest';
import { parseThing } from './thing';

describe('parseThing', () => {
  it('accepts + normalises', () => {
    const r = parseThing(' raw ');
    expect(r.ok).toBe(true);
    if (r.ok) expect(r.value).toBe('RAW');
  });
  it('rejects malformed', () => {
    expect(parseThing('').ok).toBe(false); // asserts the tag, not the copy
  });
});

Worked examples

  • src/app/registratie/domain/value-objects/postcode.spec.ts — parser style.
  • src/app/registratie/infrastructure/brp.adapter.spec.ts — trust boundary (null/{}).
  • src/app/registratie/domain/registratie-wizard.machine.spec.ts — pure reducer.

Verify

npm test                     # Vitest (ng test — no vitest.config)
npm run test-storybook       # axe over every story (UI a11y gate)
cd backend && dotnet test    # backend rule + endpoint + golden tests