Skip to content

Commit 7b2b9be

Browse files
skwowetcursoragent
andcommitted
docs: add ADR-0008 on unit test writing principles
Signed-off-by: Yeganathan S <63534555+skwowet@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent a6a92a8 commit 7b2b9be

2 files changed

Lines changed: 87 additions & 0 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# ADR-0008: How we write unit tests
2+
3+
**Date**: 2026-07-13
4+
**Status**: accepted
5+
**Deciders**: Yeganathan S
6+
7+
## Context
8+
9+
Unit tests should stay easy to read and cheap to change. Without a shared style,
10+
suites either bury the case in setup and weak assertions, or grow a private
11+
layer of helpers that obscures what is under test.
12+
13+
We need a small set of principles for designing unit tests: what a case is,
14+
what it asserts, and what may be shared.
15+
16+
## Decision
17+
18+
Treat each unit test as a **named scenario**. Compose data at the call site,
19+
assert the outcome that proves the behavior, and extract helpers only when
20+
reuse is real.
21+
22+
### Scenarios
23+
24+
- Name the real situation under test — one decision or path per case.
25+
- Setup should make that story obvious (concrete labels, dates, relationships).
26+
- Do not combine unrelated behaviors in a single test.
27+
- Prefer fewer tests that fail clearly over many that pass for the wrong reason.
28+
29+
### Assertions
30+
31+
- Assert the result the unit owns (shape, exact values, exact counts).
32+
- Prefer exact expectations when the outcome is deterministic.
33+
- Avoid loose probes (`some`, `greaterThan`, optional finds) unless the claim
34+
truly is approximate.
35+
36+
### What to share
37+
38+
| Layer | Where | Belongs |
39+
| --- | --- | --- |
40+
| Scenario | Inside the test | Data that defines *this* case |
41+
| File-local | Top of the suite file | Constants or tiny helpers used more than once in this file |
42+
| Shared kit | `@crowd/test-kit` | Helpers needed by a second suite |
43+
44+
**Rules of thumb**
45+
46+
- Start inline. Extract to file-local only after real repetition.
47+
- Promote to the shared kit only when another suite needs the same helper —
48+
and keep it sharp (create / fill), not a full scenario builder.
49+
- Shared fixtures at the top of a file should be stable shapes, not multipurpose
50+
objects that different tests reinterpret by index.
51+
- A few duplicated lines that tell the story beat a shared fixture that hides it.
52+
- Reuse production types and enums. Do not invent test-only mirrors.
53+
- Name helpers after what they do, not after a vague dump of domain data.
54+
- A short list of clear file-local helpers is fine; wrappers that only pass data
55+
through are not.
56+
57+
## Alternatives Considered
58+
59+
### Alternative 1: Maximize reuse via shared scenario builders
60+
61+
- **Pros**: Short call sites.
62+
- **Cons**: Hides what each test owns; failures are harder to diagnose.
63+
- **Why not**: Readability of the scenario matters more than shorter setup.
64+
65+
### Alternative 2: No shared guidance — each suite invents its own style
66+
67+
- **Pros**: Maximum local freedom.
68+
- **Cons**: Inconsistent quality; hard to review or extend.
69+
- **Why not**: A thin shared philosophy scales better than ad-hoc patterns.
70+
71+
## Consequences
72+
73+
### Positive
74+
75+
- Tests read as stories; failures point at a clear decision.
76+
- Sharing stays deliberate.
77+
78+
### Negative
79+
80+
- Some tests look more verbose than a “build everything” helper.
81+
82+
### Risks
83+
84+
- File-local helpers slowly become a private framework.
85+
**Mitigation:** extract only on repetition; prefer deleting a wrapper over
86+
stacking another.

docs/adr/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Use the `/adr` skill in Claude Code to record new ADRs or query past decisions.
1414
| [ADR-0005](./0005-pypi-downloads-bigquery-merge-scoping.md) | PyPI downloads via BigQuery bulk export, scoped in the Postgres merge | accepted | 2026-07-01 |
1515
| [ADR-0006](./0006-database-schema-types-as-source-of-truth.md) | Database schema types as the source of truth | accepted | 2026-07-09 |
1616
| [ADR-0007](./0007-test-factory-primitives-and-defaults.md) | Test factory primitives and defaults | accepted | 2026-07-10 |
17+
| [ADR-0008](./0008-how-we-write-unit-tests.md) | How we write unit tests | accepted | 2026-07-13 |
1718

1819
## Why ADRs?
1920

0 commit comments

Comments
 (0)