diff options
| author | Craig Jennings <c@cjennings.net> | 2026-04-25 01:31:16 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-04-25 01:31:16 -0500 |
| commit | 68c4690cbcb0bd5e8bf6a44f1c07b25d2141c5a4 (patch) | |
| tree | 43c8d20535c823685afa7719799e8c5b4eb94e5c /claude-rules | |
| parent | 20de0eca58802bc778b5a0103c6b910762b447a4 (diff) | |
| download | rulesets-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')
| -rw-r--r-- | claude-rules/testing.md | 22 |
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 |
