The consent module was built testable, it just never got tested
My consent-first analytics package has a small module, consent.ts, that reads and writes a visitor’s tracking decision to localStorage and checks Global Privacy Control. The comment at the top of the file has said the same thing since I wrote it: storage and navigator are passed in as parameters instead of touched directly, specifically so this module is testable outside a browser.
That comment sat there for months while the module had zero tests. I finally wrote them, right after doing the same exercise on a sibling package. Thirteen tests, three functions.
What actually needed covering
readConsent has more failure modes than its five lines suggest. Nothing stored. Corrupted JSON. A version number that doesn’t match, which I bump whenever what I track changes so old decisions correctly re-prompt. The version field missing outright. A decision value that isn’t "granted" or "denied". And a decision that’s explicitly null rather than missing.
Every one of those has to return null quietly, with no exception reaching the caller. I also had the storage read itself throw, to exercise the try/catch wrapping the whole function - private browsing modes and some quota situations do exactly that.
writeConsent got a version that captures every call it makes with a tiny recording fake instead of a real Storage object, so I could assert on the exact key and the exact JSON payload it stores. Parsed back into an object rather than compared as a raw string, since string equality on JSON output pins key ordering I never promised. Plus a throwing-storage test for the write side, since that function deliberately swallows the error: a full-storage visitor’s decision still applies for the current page, they just get asked again next visit.
The test that would have caught a real bug
Every test up to that point exercised one function against a fake I controlled directly. None of them proved the two functions actually agree with each other - that whatever writeConsent puts into storage is exactly what readConsent expects to find.
So I added one more: write a decision, read it back from the same fake storage, check it survived the round trip. It’s the one test that catches a key mismatch or a version-field disagreement between the two functions, and it’s also the shortest test in the file. Testing the right thing usually costs less than testing thoroughly around it.
What I left out on purpose
The module that boots the actual prompt and gate in the browser - DOM manipulation, event listeners, the visible UI - didn’t get touched. That needs jsdom or a real browser to test meaningfully, and pure-function coverage was the free win sitting right there. Scoping it out explicitly, rather than letting it creep in, kept this a same-day change instead of a multi-day one.
Lesson
A comment explaining why a module is designed to be testable is not the same as the module being tested. I’d read that comment plenty of times while working in the file and never once treated it as a todo. It took doing the identical exercise on a different package for the itch to turn into actual tests here.
Related reading
astro-blog finally got tests, and they found a real bug
Five pure-function modules, a timezone trap, and an import that worked in every consuming site because a bundler had been quietly covering for it.
The escaping bug that only shows up with a second query param
A rewrite function that worked in every test and every real URL it had ever seen, and would still have broken the moment someone added a second query param.
Testing the part of the codebase that documents its own footgun
A README warning about a subtle failure mode is a confession that the code isn't tested against it yet.