Testing strategy

Dark controls: testing that a gate says no

Almost every test suite checks that the right thing works: the logged-in user sees their dashboard, the paid plan unlocks the feature, the valid form submits. Far fewer check the other half — that the wrong thing is refused. The logged-out user doesn't see the dashboard. The free plan doesn't unlock the feature. The malformed form doesn't submit. We call a check on that other half a dark control: a test whose entire job is to prove that something which must render nothing, in fact renders nothing.

Why the positive case isn't enough

A gate — an auth check, a paywall, a feature flag, a permission boundary, a rate limit — has exactly two behaviors that matter: it lets the right thing through, and it stops the wrong thing. Testing only the first behavior tells you the gate works when everything is configured correctly and nobody's trying to get around it. It tells you nothing about what happens when someone is. A login wall that correctly shows a dashboard to a valid session and also shows the same dashboard to no session at all will pass every test suite that only ever logs in correctly and checks what appears — right up until it doesn't, in production, for someone who isn't supposed to be there.

This isn't hypothetical caution. It's the natural failure mode of the positive-only test: nobody wrote the assertion "and it should NOT show up," so nothing catches the day it does. A suite full of green checks that only ever tried the happy path can coexist for a long time with a gate that's silently open.

What makes a dark-control test hard to get right

The obvious version — "load the page while logged out, assert the dashboard isn't there" — is a start, but it's easy to write one that passes for the wrong reason. A page that's still loading hasn't rendered the dashboard yet either; that's not the same claim as "the dashboard is correctly withheld." A network error, a redirect loop, a client-side crash before the gate check even runs — all of these also produce "the dashboard isn't there," and all of them are a different bug wearing the passing test's clothes.

A dark control that's actually trustworthy has to prove three things, not one: that the forbidden content is absent, that the page reached a real, settled state (not a loading spinner or an error boundary standing in for "nothing to see"), and that the absence is because the gate refused, not because something upstream broke before the gate ever ran. Skip the second and third checks, and a dark control degrades into exactly the kind of check the UNMEASURED state exists to catch — a result that looks like a verdict but isn't one.

How this shows up as a generated test

When a mapping session records a flow that involves a gate — a sign-in wall, a plan-gated feature, an admin-only page — the negative case is usually a second flow, not an afterthought bolted onto the first: drive the app into the state that should be refused (logged out, wrong plan, wrong role), reach a settled page, and assert on absence with the same rigor as any positive assertion — the forbidden element isn't in the accessibility tree, not just invisible; the page title and URL match what a refusal actually looks like, not a generic error page; and nothing upstream errored before the gate had a chance to run.

The value of writing this as an explicit flow, rather than trusting that "we'd notice," is the same value a screenshot-and-trace-backed run gives any other test: when a dark control ever does fail — the gate started leaking — you get a trace of exactly what a logged-out user actually saw, not a bug report someone has to reproduce from a hunch.

The rule, stated plainly

A gate that has only ever been tested saying yes is not a gate you've tested. If a check can render something that must not appear, write the test that proves it doesn't — settled state, real absence, no upstream error standing in for a refusal — and treat that test as no less important than the one proving the door opens for the right person.


Related: PASS / FAIL / UNMEASURED: why a QA verdict needs a third state · Why we read the screen with OCR instead of trusting selectors · How failures become bugs