September 12, 2026
A TEST IS A CLAIM WITH EVIDENCE

You already know how to slice work, write a spec, and put AI behind contracts and review gates. Now comes the uncomfortable question: does the thing it built actually do what the card says? A demo in your own browser is not an answer. A test is.
A test is a repeatable check, not a vibe
Here is the definition to carry through all of Class 50:
A test is a repeatable check of expected behavior. Same starting state, same action, same observable result — every time anyone runs it.
That rules out three things people routinely mistake for testing:
- Code coverage percentage. It tells you which lines ran, not whether the behavior was correct. A watchlist route can have 100% coverage and still let anyone delete anyone else's items.
- A screenshot. It shows what one screen looked like once. It does not prove the save persisted, the denial worked, or the same flow passes tomorrow.
- "I tested everything." From a person or an AI, that sentence is a claim without evidence. Ask the follow-up every time: which behavior, from which starting state, with what result, and where is the record?
Think of it like a courtroom. The requirement is the claim: "a signed-out visitor who clicks Save is asked to sign in, and nothing is written." The test is the evidence: a procedure anyone can re-run that shows the claim holding. No procedure, no proof.
Behavior first: Given / When / Then
The simplest way to write a check worth running is one sentence with three parts:
Given [starting state], when [action], then [observable result].
Take one Research Desk requirement — *"a signed-in user can save a company to their private watchlist"* — and turn it into four cases. Notice that the interesting testing work is not the happy path. It is the other three:
| Case | Given / When / Then |
|---|---|
| Normal | Given a signed-in user with an empty watchlist, when they save AAPL, then the watchlist contains AAPL and the UI shows a saved confirmation. |
| Empty | Given a signed-in user, when they search for a ticker with no available brief (say ZZZZ), then they see an honest empty state — "no brief available" — and no watchlist write happens. |
| Error | Given a signed-in user viewing AAPL, when the data provider times out during refresh, then they see "data unavailable — try again shortly," the failure is logged with a request ID, and no partial record is saved. |
| Unauthorized | Given a signed-out visitor (or User A acting on User B's list), when they attempt to save to the watchlist, then the request is denied, nothing is written, and the visitor is routed to sign-in. |
Each row names something observable: a row in the list, a message on screen, a denied write, a log event. "The function runs without crashing" is not observable enough. "The watchlist contains exactly one AAPL entry owned by this user" is.
Neighborhood Events makes the same point with less finance: Given Maya is signed in as the organizer of the block party, when she edits its time, then the listing shows the new time. Given a different organizer, when he submits the same edit, then he is denied and the listing is unchanged. The second sentence is the test most AI-built apps are missing.
The portfolio: five kinds of proof
Beginners often ask "should I write a unit test or an end-to-end test?" as if it were one choice. A working product needs a small portfolio, because each kind of check proves something different:
1. Fast focused checks (unit). One small rule in isolation: ticker normalization, a date formatter, a permission function. They run in milliseconds and tell you exactly which rule broke. Lesson 50.2 lives here. 2. Boundary checks (integration). Two or more pieces working together across a seam: route plus authorization plus database policy, using a safe test database. They catch the failures that live where reasonable pieces meet. Lesson 50.3 lives here. 3. User-flow checks in a browser (end-to-end). A real browser drives the journey: sign in, search, save, refresh, sign out, confirm denial. Slow and genuinely reassuring when the journey is the product. See Playwright's introduction for the current vocabulary and tooling. 4. Manual exploration. A person with judgment pokes at the preview: odd inputs, small screens, keyboard-only use, confusing copy, visual breakage. Automation proves the expected; humans find the surprise. 5. Production checks (smoke). Tiny probes against the deployed app: homepage loads, sign-in works, one critical write succeeds, error rates look sane. They prove the live system, not the laptop.
No layer substitutes for another. A hundred fast checks do not prove the ownership rule survives the API route. One browser journey does not diagnose which function broke. The skill is choosing the *smallest* proof for the risk in front of you — which is exactly what your TEST-PLAN.md records.
Regression: every bug buys a test
A regression is previously working behavior that breaks after a change. AI workers are prolific regression factories: a "small cleanup" renames a field, and the save button silently stops persisting.
The discipline is short and non-negotiable:
Found that a signed-out save wrote a row instead of denying? Fix the route, then add the unauthorized-case test from the table above. Next time an agent refactors the route, that test fails before the bug ships. The bug paid tuition; the test keeps the lesson.
Doubles and fixtures: stable stage props
Two terms, plainly:
- A fixture is controlled sample data a test uses: a fake
ResearchBriefforAAPL, two test users with known IDs, a seeded watchlist. - A test double is a stand-in for something real: a fake provider that returns the fixture instantly instead of calling a paid API over the network.
They make tests fast and stable — no flaky network, no surprise bills, no dependence on today's market data. But remember their limit, because Lesson 50.4 returns to it: a test against a double proves your code handles the *fixture*, not that the live provider is available or correct. Write that limit down in the plan. Honest boundaries are the difference between evidence and theater.
Exercise: write TEST-PLAN.md for one slice
Pick the capstone slice from Part XI: a signed-in person saves one company to a private watchlist and sees it on refresh. Pull three acceptance checks from its task card and do the following:
1. Rewrite each as a Given/When/Then statement covering normal, empty/error, and unauthorized cases. 2. Assign each a test type (focused, boundary, browser, manual, production) and one sentence saying why that type is the smallest proof. 3. Name the fixtures and doubles each test needs (test users A and B, seeded brief, fake provider). 4. For each test, write what it does not prove.
Finish line: a TEST-PLAN.md for the slice with a table like this:
| # | Given / When / Then | Type | Fixtures | Proves | Does not prove |
|---|---|---|---|---|---|
| 1 | Given signed-in User A..., when they save AAPL..., then... | boundary (route+authZ+DB) | users A/B, test DB | ownership enforced end of route | live provider availability |
Verify fast: re-read each Then and ask, "could this pass while the bug I fear still exists?" If yes, tighten the assertion. Common failure: Then-statements like "returns success" with no check on what was written, to whom, or what the other user sees.
Check your understanding
1. Why is "the AI said it tested everything" not evidence? 2. Write the unauthorized Given/When/Then for "User A tries to delete User B's watchlist item." 3. Which test type proves the ownership rule survives the API route — a focused unit check or a boundary check? Why? 4. What does a test double prove, and what does it deliberately not prove?
Next
You have the grammar of proof. Next, Lesson 50.2 zooms into the smallest layer: focused unit checks and component checks — what makes one valuable, and how AI fills your repo with brittle ones if you let it.
ARTICLE DISCUSSION
JOIN THE
CONVERSATION.
Got a question, a take, or a better way to do this? Log in and leave a comment.
