Development & Code · master area
Unit Testing
Smallest unit, fastest feedback. Single function or module, milliseconds to run. The chain's first catch — and the layer where domain logic is written down twice.
Owners: Developer Phase it lives in: How We Build (Volume IV) The corpus principle this enacts: Unit tests are executable specs.
Where it lives in the chain
- How We Build · Testing as Chain Verification — the canon: unit tests as executable specs
- How We Build · The Pipeline · seven stages — where unit tests sit in the chain of catches
How to do this
- Practice — name the test after the scenario from amigos, not the implementation.
gradeExam_clamps_balance_at_zero_for_negative_input, nottest_gradeExam_3. - Practice — one assertion per test where possible. If the test fails, the failure message tells the developer which behaviour broke without running the debugger.
- Practice — pure domain functions are tested without mocks. A unit test that needs three mocks to run is testing the wrong unit.
What good practice looks like
The unit test reads like the scenario. The scenario said:
Given Gal has an exam with three negative-scored answers
When she submits the exam
Then her balance is clamped at zero and surfaced in redThe test:
it('clamps negative balance at zero when submitting an exam', () => {
const exam = examWith({ scores: [-5, -3, -2] })
expect(submitExam(exam).balance).toBe(0)
})The test name is the spec. The body is the proof. When the wallet-balance bug surfaces, the test that should have caught it is the test that was never written — that absence is what the chain-aware label scenario-gap names.
The discipline: the unit test layer holds the brief's logic; the integration layer holds the brief's flow; the contract layer holds the brief's promise to other systems. Each layer catches a different chain level.
Related crafts
- Amigos Session — where scenarios become tests
- Contract Testing — the boundary layer
- Domain Language in Code — what the test names use