Harness Testing
A harness test runs your real code inside a wrapper that simulates the runtime just enough to
verify behavior: fake inputs, mocked services, controlled env, a seeded in-memory database, fake
timers, and setup/teardown. Instead of testing one function in isolation, you plug a larger unit —
a request handler, a worker, a screen-level flow — into a realistic-but-controlled environment and
assert what it actually does.
Use it for behavior that only appears when code is wired up: auth and status codes, payload assembly,
branching, persistence, retries, error handling. Keep pure-logic unit tests for isolated helpers.
The one rule: non-invasive
Never change production code to make it testable. No dependency-injection refactors, no "export
the handler just for tests", no test-only flags in shipped code. A harness that demands production
changes isn't riskless and won't be adopted for the code that matters most. Intercept a seam
instead (below). If you genuinely cannot reach a seam without touching production, that's a design
finding to raise — not a reason to weaken the rule.
Step 1 — Find the one seam
A harness is only as simple as its interception point. Find the single boundary that every external
dependency crosses, and mock there — not once per dependency.
- Outbound network is usually the seam: most SDKs (DB clients, payment, email, storage) ultimately
call the platform's HTTP primitive. Intercept that and you mock them all with one mechanism.
- TypeScript/Deno/Node: replace
globalThis.fetch (verify your SDKs use it — many do in modern
runtimes), or use nock/msw.
- Python:
responses/respx, or monkeypatch requests/httpx.
- Go: inject an
http.RoundTripper / httptest.Server.
- The entrypoint is the other seam: capture the handler the framework would serve, and call it
with a fake request — without starting a server. (E.g. intercept the serve call to grab the handler
closure; build a fake
Request; assert on the returned Response.)
- The clock: swap in fake timers so time-dependent logic (timeouts, retries, TTLs) is deterministic.
One seam = one mental model. Resist per-dependency mocks; they drift and multiply.
Step 2 — Build the harness toolkit
Small, composable, single-purpose pieces:
loadUnit(...) — boot the real handler/component/job (intercepting the entrypoint, not editing it).
router(routes) — match method + url → canned response, and record every call for assertions.
seededStore(data) — translate an in-memory dataset into the responses the data layer expects;
record writes so tests can assert persistence.
withEnv(vars) — snapshot, set, and restore environment variables.
makeRequest(...) — build fake inbound requests.
createHarness({...}) — wire the above + fake timers, expose request() and the recorders, and
register one teardown().
Step 3 — Write the test
Arrange (env + seed + routes) → boot the real unit → act (one fake request / one render) → assert on
the real output and the recorded outbound calls → teardown() in finally.
// Illustrative (TypeScript). Adapt the seam to your stack.
const h = await createHarness({
unit: './handlers/checkout', // booted, not modified
env: { SERVICE_KEY: 'test', API_BASE: 'https://api.test' },
db: { tables: { orders: [], items: [{ id: 'i1', price: 100 }] } },
routes: [{ method: 'POST', url: 'api.payments.test', respond: () => json({ id: 'pay_1' }) }],
});
try {
const res = await h.request({ method: 'POST', url: '/checkout', body: { itemId: 'i1' } });
expect(res.status).toBe(200);
expect(h.db.tables.orders).toHaveLength(1); // persistence
expect(h.calls.some((c) => c.url.includes('payments'))).toBe(true); // outbound
} finally {
h.teardown();
}
Principles (the difference between a harness that helps and one that lies)
- Boot the real thing. Mock dependencies, never the unit under test. The value is exercising
real code against fakes — not re-implementing its logic in the test.
- Loud unmatched, never silent. An unhandled dependency call must throw with a clear message,
not return
undefined or hang. Silent gaps produce green tests that assert nothing.
- Seed lazily, tear down completely. Read seeded data at execution time so tests arrange before
acting. One
teardown() reverts every global you touched (network, entrypoint, env, timers) in
reverse order; call it in finally. Cross-test leakage is the #1 harness failure mode.
- Pin actual behavior, not assumed behavior. The first boot will surprise you — that's the point.
Assert what the code does (the real status code, the real error text), record the surprise, and
don't quietly "fix" production to match your assumption.
- Keep the fake minimal; extend on demand. Emulate only the verbs the unit uses. A smaller fake
is easier to trust. Add a verb deliberately when a test needs it — never loosen matching to pass.
- Own only what you can clean up. Real clients start background work (refresh timers, pools) the
unit never disposes because the runtime tears it down. Disable leak/resource sanitizers for harness
tests specifically rather than editing production to satisfy a detector.
- Fast, separate, gated. No real I/O. Name harness tests distinctly (e.g.
*.harness.test.*) so
the unit suite stays quick, and fold them into the one verification command the whole team and
every agent runs — so they're backpressure that rejects regressions, with CI as the mechanical
ratchet.
Anti-patterns
- A test that passes whether or not the code under test runs (over-mocked, asserts nothing real).
- Editing production code "just a little" for testability — start over from a seam.
- A bespoke mock per dependency — collapse to one seam.
- Matching
* / catch-all routes everywhere — fine for a "did it reach real work" smoke, but specific
routes are what let you assert the right calls happened.
Background reading
Anthropic — Harness Design for Long-Running Apps; Effective Harnesses for Long-Running Agents.
OpenAI — Harness Engineering. Geoffrey Huntley — Ralph Wiggum as a Software Engineer.
celesteanders/harness — docs/best-practices.md.
See references/checklist.md for a copy-pasteable pre-flight checklist.
1---2name: harness-testing3description: Use when writing, designing, or strengthening harness tests — tests that boot a larger unit of a system (an HTTP handler, a job, a UI flow) into a realistic but controlled environment (fake requests, mocked services/APIs, controlled env vars, an in-memory/seeded database, fake timers) and assert end-to-end behavior. Use when asked to "test the whole handler", "integration test without a real DB/network", "mock the services and run the real code path", "add a test harness", or to improve reliability of code that only misbehaves when wired to its dependencies. Framework-agnostic; includes notes for TypeScript/Deno/Node, Python, and Go.4---56# Harness Testing78A **harness test** runs your real code inside a wrapper that simulates the runtime just enough to9verify behavior: fake inputs, mocked services, controlled env, a seeded in-memory database, fake10timers, and setup/teardown. Instead of testing one function in isolation, you plug a *larger* unit —11a request handler, a worker, a screen-level flow — into a realistic-but-controlled environment and12assert what it actually does.1314Use it for behavior that only appears when code is wired up: auth and status codes, payload assembly,15branching, persistence, retries, error handling. Keep pure-logic unit tests for isolated helpers.1617## The one rule: non-invasive18**Never change production code to make it testable.** No dependency-injection refactors, no "export19the handler just for tests", no test-only flags in shipped code. A harness that demands production20changes isn't riskless and won't be adopted for the code that matters most. Intercept a *seam*21instead (below). If you genuinely cannot reach a seam without touching production, that's a design22finding to raise — not a reason to weaken the rule.2324## Step 1 — Find the one seam25A harness is only as simple as its interception point. Find the single boundary that *every* external26dependency crosses, and mock there — not once per dependency.2728- **Outbound network** is usually the seam: most SDKs (DB clients, payment, email, storage) ultimately29 call the platform's HTTP primitive. Intercept that and you mock them all with one mechanism.30 - TypeScript/Deno/Node: replace `globalThis.fetch` (verify your SDKs use it — many do in modern31 runtimes), or use `nock`/`msw`.32 - Python: `responses`/`respx`, or monkeypatch `requests`/`httpx`.33 - Go: inject an `http.RoundTripper` / `httptest.Server`.34- **The entrypoint** is the other seam: capture the handler the framework would serve, and call it35 with a fake request — without starting a server. (E.g. intercept the serve call to grab the handler36 closure; build a fake `Request`; assert on the returned `Response`.)37- **The clock**: swap in fake timers so time-dependent logic (timeouts, retries, TTLs) is deterministic.3839One seam = one mental model. Resist per-dependency mocks; they drift and multiply.4041## Step 2 — Build the harness toolkit42Small, composable, single-purpose pieces:43441. `loadUnit(...)` — boot the real handler/component/job (intercepting the entrypoint, not editing it).452. `router(routes)` — match `method + url → canned response`, and **record every call** for assertions.463. `seededStore(data)` — translate an in-memory dataset into the responses the data layer expects;47 record writes so tests can assert persistence.484. `withEnv(vars)` — snapshot, set, and restore environment variables.495. `makeRequest(...)` — build fake inbound requests.506. `createHarness({...})` — wire the above + fake timers, expose `request()` and the recorders, and51 register **one** `teardown()`.5253## Step 3 — Write the test54Arrange (env + seed + routes) → boot the real unit → act (one fake request / one render) → assert on55the real output *and* the recorded outbound calls → `teardown()` in `finally`.5657```ts58// Illustrative (TypeScript). Adapt the seam to your stack.59const h = await createHarness({60 unit: './handlers/checkout', // booted, not modified61 env: { SERVICE_KEY: 'test', API_BASE: 'https://api.test' },62 db: { tables: { orders: [], items: [{ id: 'i1', price: 100 }] } },63 routes: [{ method: 'POST', url: 'api.payments.test', respond: () => json({ id: 'pay_1' }) }],64});65try {66 const res = await h.request({ method: 'POST', url: '/checkout', body: { itemId: 'i1' } });67 expect(res.status).toBe(200);68 expect(h.db.tables.orders).toHaveLength(1); // persistence69 expect(h.calls.some((c) => c.url.includes('payments'))).toBe(true); // outbound70} finally {71 h.teardown();72}73```7475## Principles (the difference between a harness that helps and one that lies)76771. **Boot the real thing.** Mock *dependencies*, never the unit under test. The value is exercising78 real code against fakes — not re-implementing its logic in the test.792. **Loud unmatched, never silent.** An unhandled dependency call must *throw* with a clear message,80 not return `undefined` or hang. Silent gaps produce green tests that assert nothing.813. **Seed lazily, tear down completely.** Read seeded data at execution time so tests arrange before82 acting. One `teardown()` reverts every global you touched (network, entrypoint, env, timers) in83 reverse order; call it in `finally`. Cross-test leakage is the #1 harness failure mode.844. **Pin actual behavior, not assumed behavior.** The first boot will surprise you — that's the point.85 Assert what the code *does* (the real status code, the real error text), record the surprise, and86 don't quietly "fix" production to match your assumption.875. **Keep the fake minimal; extend on demand.** Emulate only the verbs the unit uses. A smaller fake88 is easier to trust. Add a verb deliberately when a test needs it — never loosen matching to pass.896. **Own only what you can clean up.** Real clients start background work (refresh timers, pools) the90 unit never disposes because the runtime tears it down. Disable leak/resource sanitizers for harness91 tests specifically rather than editing production to satisfy a detector.927. **Fast, separate, gated.** No real I/O. Name harness tests distinctly (e.g. `*.harness.test.*`) so93 the unit suite stays quick, and fold them into the one verification command the whole team and94 every agent runs — so they're backpressure that rejects regressions, with CI as the mechanical95 ratchet.9697## Anti-patterns98- A test that passes whether or not the code under test runs (over-mocked, asserts nothing real).99- Editing production code "just a little" for testability — start over from a seam.100- A bespoke mock per dependency — collapse to one seam.101- Matching `*` / catch-all routes everywhere — fine for a "did it reach real work" smoke, but specific102 routes are what let you assert the *right* calls happened.103104## Background reading105Anthropic — *Harness Design for Long-Running Apps*; *Effective Harnesses for Long-Running Agents*.106OpenAI — *Harness Engineering*. Geoffrey Huntley — *Ralph Wiggum as a Software Engineer*.107celesteanders/harness — `docs/best-practices.md`.108109See `references/checklist.md` for a copy-pasteable pre-flight checklist.