Files
Panama/user/agents/skills/tdd/MOCKING.md
T
Gabriel Brown 89761a7da3 Keep the personal half of the desktop in one place, and ask before installing it
Agent instructions, skills, SSH host aliases and expansion triggers are worth
having identical on every machine one person owns, and belong in none of the
shared configuration. They live in user/ now, with a manifest saying where each
piece goes and a link-user stage that puts it there.

That stage does nothing unless the machine said yes. Somebody who clones Panama
to try the desktop keeps their own ~/.claude/CLAUDE.md exactly where it was;
the question names the destinations and defaults to no. Anything displaced goes
to config/old rather than being deleted.

~/.claude/CLAUDE.md and ~/.codex/AGENTS.md were byte-identical copies of one
file, which is the drift this exists to prevent.

Also adds the vitals toggles for the battery and Claude usage readouts, which
had preferences and no way to reach them.
2026-08-22 08:54:43 -04:00

60 lines
2.2 KiB
Markdown

# When to mock
Mock at **system boundaries** only:
- External APIs you do not control (Jira, Stripe, email, Infisical)
- Time and randomness
- Databases, sometimes, though a real local test instance is better where one exists
- The filesystem, sometimes
Do **not** mock your own modules, internal collaborators, or anything you control. A test that mocks
what you own is asserting on your own wiring rather than on behaviour, and it will break on the first
refactor that changes nothing a caller can see.
Before mocking any dependency, understand what it actually does. A mock built on a guess about a
dependency's side effects encodes the guess into the suite.
## Designing for substitutability
### Accept dependencies, do not construct them
```typescript
// Easy to substitute at the seam
function processPayment(order: Order, paymentClient: PaymentClient) {
return paymentClient.charge(order.total);
}
// Welded in: nothing can stand in for the client
function processPayment(order: Order) {
const client = new StripeClient(process.env.STRIPE_KEY);
return client.charge(order.total);
}
```
### Prefer an SDK-shaped interface over a generic fetcher
One function per external operation, rather than a single generic call with conditional logic:
```typescript
// GOOD: each operation is independently substitutable
const api = {
getUser: (id: string) => fetch(`/users/${id}`),
getOrders: (userId: string) => fetch(`/users/${userId}/orders`),
createOrder: (data: NewOrder) => fetch("/orders", { method: "POST", body: data }),
};
// BAD: the stand-in needs conditional logic to know what it is being asked for
const api = {
fetch: (endpoint: string, options: RequestInit) => fetch(endpoint, options),
};
```
The SDK shape means each stand-in returns one specific type, no branching in test setup, an obvious
read of which endpoints a test exercises, and type safety per operation.
## One adapter is not a seam
Introducing a port because a single test wants to substitute at it is indirection, not design. One
adapter means a hypothetical seam; two means a real one, and production-plus-test counts as two. Call
the Skill tool with "codebase-design" when the seam placement itself is in question.