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.
98 lines
3.1 KiB
Markdown
98 lines
3.1 KiB
Markdown
# What a good test is
|
|
|
|
A test verifies behaviour through a public interface. The code underneath can change entirely; the
|
|
test should not. A good one reads like a specification: `user can checkout with valid cart` tells you
|
|
exactly what capability exists, and survives refactors because it does not care about internal
|
|
structure.
|
|
|
|
## Good
|
|
|
|
```typescript
|
|
test("user can checkout with valid cart", async () => {
|
|
const cart = createCart();
|
|
cart.add(product);
|
|
|
|
const result = await checkout(cart, paymentMethod);
|
|
|
|
expect(result.status).toBe("confirmed");
|
|
});
|
|
```
|
|
|
|
- Tests what a caller cares about
|
|
- Uses the public interface only
|
|
- Survives internal refactors
|
|
- Describes *what*, not *how*
|
|
- One logical assertion
|
|
|
|
## The three anti-patterns
|
|
|
|
### Implementation-coupled
|
|
|
|
Mocks internal collaborators, reaches for private methods, or asserts on call counts and ordering.
|
|
**The tell: the test breaks when you refactor, even though the behaviour did not change.**
|
|
|
|
```typescript
|
|
// BAD: asserts on the mock, not the behaviour
|
|
test("checkout calls paymentService.process", async () => {
|
|
const mockPayment = vi.mock(paymentService);
|
|
await checkout(cart, payment);
|
|
expect(mockPayment.process).toHaveBeenCalledWith(cart.total);
|
|
});
|
|
```
|
|
|
|
A second form is verifying through a side channel instead of the interface:
|
|
|
|
```typescript
|
|
// BAD: bypasses the interface to check the database directly
|
|
test("createUser saves to database", async () => {
|
|
await createUser({ name: "Alice" });
|
|
const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]);
|
|
expect(row).toBeDefined();
|
|
});
|
|
|
|
// GOOD: verifies through the interface a caller would use
|
|
test("createUser makes the user retrievable", async () => {
|
|
const user = await createUser({ name: "Alice" });
|
|
const retrieved = await getUser(user.id);
|
|
expect(retrieved.name).toBe("Alice");
|
|
});
|
|
```
|
|
|
|
### Tautological
|
|
|
|
The expected value is recomputed the way the code computes it, so the test passes by construction and
|
|
can never disagree with the implementation.
|
|
|
|
```typescript
|
|
// BAD: the assertion reimplements the function
|
|
test("calculateTotal sums line items", () => {
|
|
const items = [{ price: 10 }, { price: 5 }];
|
|
const expected = items.reduce((sum, i) => sum + i.price, 0);
|
|
expect(calculateTotal(items)).toBe(expected);
|
|
});
|
|
|
|
// GOOD: an independent, known literal
|
|
test("calculateTotal sums line items", () => {
|
|
expect(calculateTotal([{ price: 10 }, { price: 5 }])).toBe(15);
|
|
});
|
|
```
|
|
|
|
Expected values come from an independent source of truth: a known-good literal, a worked example, or
|
|
the spec. Never from the code.
|
|
|
|
### Horizontal slicing
|
|
|
|
Writing all the tests first, then all the implementation. Covered in `SKILL.md`; the fix is vertical
|
|
slices, one test and one implementation at a time.
|
|
|
|
## Before writing any test
|
|
|
|
Name the production change that would make this test fail. If you cannot name one, the test asserts
|
|
nothing and should not be written.
|
|
|
|
## Keep test-only code out of production
|
|
|
|
A method that exists so a test can reach inside is a hole in the interface. Put helpers in test
|
|
utilities. If the test cannot reach what it needs through the interface, that is a design finding,
|
|
not a reason to widen the interface.
|