Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Tests across boundaries: assert behavior, not implementation text

Last updated: 5 Oct 20266 min read
tutorial
IntermediateBy AITrove Editorial

A useful web test follows an observable contract: an input request, an expected response, and whether state changed. Unit tests can check a small parser or validator, while integration tests should exercise the route with its storage and authorization boundary. A browser test is valuable for keyboard flow and visible errors that a server unit test cannot see. Avoid tests that merely repeat the same condition in test code; they can pass while the actual endpoint wiring is broken. The Node assertions below test a compact handler's visible status and storage effects. A real project should run a smaller number of full-route tests with an isolated database and a browser check for the form.

Case study

A developer changes the case route so an unauthorized reviewer gets 200 with an empty body. A test that only looks for 'no exception' would pass; a contract test asserting 403 and no storage change catches the regression. A separate browser test submits the form by pressing Enter, follows the error summary, and confirms that safe input remains. One test per boundary gives clearer failure diagnosis than a giant test that loads the page, writes data, sends email, and checks a chart in one step. Fixtures should use distinct case IDs to avoid cross-test collisions.

Working contract

javascript
const assert = require("node:assert/strict");
const storedNotes = new Map();
function submitNote(assigned, caseId, note) {
  if (!assigned.has(caseId)) return { status: 403 };
  if (!note.trim()) return { status: 422 };
  storedNotes.set(caseId, note.trim());
  return { status: 201 };
}
assert.equal(submitNote(new Set([47]), 83, "checked").status, 403);
assert.equal(storedNotes.has(83), false);
assert.equal(submitNote(new Set([47]), 47, " ").status, 422);
assert.equal(submitNote(new Set([47]), 47, "seal checked").status, 201);
assert.equal(storedNotes.get(47), "seal checked");
console.log("4 boundary assertions passed");

Observed output

Output
4 boundary assertions passed

Cost and tradeoffs

The sample tests run in O(T) assertions for T fixed cases and O(N) memory for the in-memory records. Integration tests add database and network setup cost; browser tests add startup and rendering time. Run fast deterministic checks on every change and the slower critical journey at appropriate gates. A fake can verify a pure policy, but it cannot prove the real route, cookie, or persistence wiring. Every test should state which boundary it covers and which failure it is meant to catch.

Common Mistakes

  • Do not assert only that a function returned without throwing.
  • Do not mock the route layer in the only route contract test.
  • Do not share mutable test data across cases without reset.
  • Do not let a browser-only test replace focused server authorization checks.

Continue through the stack

Form submission: validate on the server and return field errors; Authorization: check permission for this record on every request; Release checks: prove the critical route and prepare a rollback.

Failure trace

A server changes a validation response from fieldErrors to errors. Its unit tests pass, and the browser silently shows 'Something went wrong' because it expects the old field. Test a small set of request and response shapes across the actual boundary, including status, error fields, permission, and the success body. A mock that repeats the frontend's assumption cannot catch this drift.

Verification

  • Send a valid write and an invalid write to the server contract, checking status and response fields.
  • Run the browser against a deliberate changed error shape and verify a contract check fails.
  • Exercise a direct nested route on the built release, not only a client transition.

Decision note

Contract tests are most useful at stable seams. Keep them focused on externally observable behavior; implementation-detail assertions make safe refactoring costly without protecting the user.

Advanced connections

API Evolution and Compatibility Windows; Browser Journey and Fault-Injection Tests.

Further connections

Test Boundaries and Evidence Selection; Deterministic Test Doubles and Fault Injection.

Further connections

Consumer Contract Matrix and Expand-Contract Release.

web-tech
web-development
Storage details