aboutsummaryrefslogtreecommitdiff
path: root/claude-rules/testing.md
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-04-25 01:31:16 -0500
committerCraig Jennings <c@cjennings.net>2026-04-25 01:31:16 -0500
commit68c4690cbcb0bd5e8bf6a44f1c07b25d2141c5a4 (patch)
tree43c8d20535c823685afa7719799e8c5b4eb94e5c /claude-rules/testing.md
parent20de0eca58802bc778b5a0103c6b910762b447a4 (diff)
downloadrulesets-68c4690cbcb0bd5e8bf6a44f1c07b25d2141c5a4.tar.gz
rulesets-68c4690cbcb0bd5e8bf6a44f1c07b25d2141c5a4.zip
docs(testing): rewrite time-mocking helper rule to be language-agnostic
The original phrasing leaned on Lisp terms (let-bind, defvar) that don't translate to most other languages. Generalize to two named failure modes (infinite recursion against the mocked primitive, scope-shadowing that production callers can't see) with examples across Python, Lisp, Go, and JavaScript so the rule applies regardless of stack.
Diffstat (limited to 'claude-rules/testing.md')
-rw-r--r--claude-rules/testing.md22
1 files changed, 15 insertions, 7 deletions
diff --git a/claude-rules/testing.md b/claude-rules/testing.md
index a6bc1df..b2ff606 100644
--- a/claude-rules/testing.md
+++ b/claude-rules/testing.md
@@ -158,13 +158,21 @@ the specific case choice.
- Never hardcode dates or times — generate them relative to `now()`
- No reliance on test execution order
- No flaky network calls in unit tests
-- Time/clock-mocking helpers must not call the primitives they're
- mocking (infinite recursion), and must not `let`-bind over a
- `defvar` or other globally-defined symbol (the binding shadows the
- global only inside the test scope, so production code that reads the
- symbol gets the original value, not the mock — silent test miss).
- Mock by redefining at the symbol's definition site or via the
- language's first-class mocking primitive.
+- Time/clock-mocking helpers must avoid two recurring failure modes:
+ - *Infinite recursion.* The helper must not call the primitive it's
+ replacing. If the mock for `now()` calls `now()`, the test stack
+ overflows. Compute the mock value from a fixed source (a captured
+ instant, an injected fake clock).
+ - *Scope-shadowing without reach.* A mock that only exists inside
+ the test function won't affect production code that reads the
+ symbol through its canonical path. Replace the symbol at its
+ definition site (monkey-patch the module attribute in Python,
+ redefine the global in Lisp, swap the package-level binding in
+ Go, replace the named export in JavaScript) — or inject a fake
+ via dependency-inversion. Don't lean on scope-shadowing
+ primitives (Lisp `let`, Python local rebind, JS shadowed `let`)
+ that fence the mock to the test's lexical scope; production code
+ won't see them and the test passes against the real clock.
### Performance
- Unit tests: <100ms each