Next.js / Advanced: caching modes, lifecycle and release evidence / Lesson 16
Testing, observability and honest runtime evidence
Choose a test boundary that proves the claimed behavior and collect safe stage evidence for failures.
On this page
Lesson goals
- Separate pure helpers, component tests, builds and browser/server integrations.
- Assert identity, protected effects and exact output rather than status alone.
A framework claim needs the corresponding executed boundary
A local validator test proves input decisions, not Next route discovery or async request APIs. A component test proves only the rendering environment it actually uses, while async Server Components and framework server behavior may require a different supported test strategy. A build checks compilation and route production but does not exercise every browser interaction or authorization case. Read the current official guide for the chosen runner and record its limitations. Avoid installing new packages or opening a project server merely to test a pure function that can run safely with already available Node.
Retain route-specific fixtures and their evidence requirements
The JSON is a synthetic test plan, not a record of completed requests. It names source identity and effect assertions because a successful status can still contain another tenant’s card, and a denied response can follow a forbidden write. Include build/request phases, direct actions and the actual cache mode. Do not copy one generic status matrix to every endpoint. A metadata route needs public-key review, a mutation needs replay cases, and streaming needs late-failure and cleanup fixtures. Keep synthetic identities and no real tokens in artifacts used for review.
{"route":"course-card-and-rename","mode":"cacheComponents-false","cases":["exact-eligible-card","denied-principal-no-write","invalid-input-no-write","source-failure-safe-output","commit-before-lost-response","account-switch-pending-result"],"execution":"plan-only"}
The plan describes required assertions, not passing framework tests. It can be parsed offline without a server.
Prove policy precedes a protected effect locally
The local adapter accepts an injected writer and rejects a cross-tenant caller before that writer is invoked. The allowed caller causes exactly one synthetic effect. This checks sequencing in plain JavaScript, not a Server Action, session verifier or transaction. A real integration should preserve the invariant through actual input parsing, current principal resolution and source access. Testing only the returned code would miss a write performed before the denial. Keep clocks and source fakes deterministic rather than relying on arbitrary sleeps or live external state.
function rename(principal,tenant,writer) {
if(principal.tenantId!==tenant)return {code:"DENIED"};
return writer();
}
let effects=0;const writer=()=>({revision:++effects});
console.assert(rename({tenantId:"lab-b"},"lab-a",writer).code === "DENIED");
console.assert(effects===0);
console.assert(rename({tenantId:"lab-a"},"lab-a",writer).revision===1);
console.assert(effects===1);
Denied work causes zero effects and allowed work one. No Next request, credential or durable source is exercised.
Collect safe phase and dependency evidence
Record a reviewed route/operation identifier, selected build/configuration, safe outcome category, timing phase and bounded source counts. Avoid raw URLs with personal query values, tokens and full action payloads. Separate source queueing, fetch duration, render time and client work when the symptom requires it. Concurrent spans overlap, so adding every duration can misstate elapsed time. A 2xx response does not prove complete domain success, and a closed connection is distinct from a completed error body. Use controlled correlation for incident detail rather than unbounded high-cardinality user labels or private source-message dumps.
State exact execution and avoid invented integration success
Report the runtime, model names, parsed fixtures and unexecuted boundaries. A syntax check cannot prove cache isolation, and a fake response call cannot prove cookie enforcement in a browser. Broaden checks only when the change or an unresolved concern justifies them, and use approved existing tooling for real integration. This course executes the writer-spy function and parses the matrix only; it does not compile a Next app, run a unit-test package, listen on HTTP, hydrate a browser or perform a load test. The next reviewer should know precisely which framework and operational guarantees still need authorized evidence.
Common errors and fixes
A pure helper is reported as a complete Next test
- Why it happens
- The report did not distinguish the actual executed boundary.
- How to fix it
- Label local, component, build and transport evidence separately.
- Prevent it next time
- List meaningful unexecuted boundaries beside results.
A denied action still writes
- Why it happens
- Assertions checked output but omitted source effects.
- How to fix it
- Assert that the protected writer was never invoked.
- Prevent it next time
- Retain paired allowed/denied fixtures for the same object.
Telemetry exposes private query or action values
- Why it happens
- Raw transport data was used as routine diagnostic output.
- How to fix it
- Use safe bounded identifiers and controlled correlation.
- Prevent it next time
- Review exact logging fields and retention.
Check your understanding
1. Why is an async Server Component not automatically covered by a synchronous component test?
Show worked solution
The test environment may not implement the framework’s server rendering and asynchronous component protocol. Check the supported runner guidance and choose an appropriate integration for that boundary. Keep pure helper tests useful, but do not infer route execution, server cache behavior or hydration from a test that never exercised them.
2. What does the writer spy prove that a DENIED result alone does not?
Show worked solution
It proves that this local rejection branch did not invoke the protected effect. An implementation can otherwise write first and reject later. The spy still does not establish authentication or database atomicity; combine it with the actual server-entry and source tests when those integrations are authorized.
Key takeaways
- Test claims must name the boundary actually exercised.
- Identity, effects and privacy evidence matter alongside status and speed.