aboutsummaryrefslogtreecommitdiff
path: root/languages
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-04-19 13:16:07 -0500
committerCraig Jennings <c@cjennings.net>2026-04-19 13:16:07 -0500
commite50c732d7138c18749b96b57004a3e23f31bbaef (patch)
treefd3738d4f252692a06c3a4f3e162736aaf2a1b6a /languages
parent2d026369b616e51199579ff039cc34be4d5c2ef9 (diff)
downloadrulesets-e50c732d7138c18749b96b57004a3e23f31bbaef.tar.gz
rulesets-e50c732d7138c18749b96b57004a3e23f31bbaef.zip
feat(rules): port key testing principles from quality-engineer prompt
Additions to claude-rules/testing.md: - Testing pyramid proportions (70-80% unit / 15-25% integration / 5-10% e2e) - Integration Tests section: docstring must name 'Components integrated:' and mark real vs mocked; when-to-write heuristics - Signs of Overmocking: 'would the test pass if the function body was NotImplementedError?' plus three more sharp questions - Testing Code That Uses Frameworks: test your integration, not the framework itself - Test Real Code, Not Copies: never inline prod code into tests - Error Behavior, Not Error Text: test type + key values, not exact prose - If Tests Are Hard to Write, Refactor the Code: hard-to-test is a code signal, not a test signal; extract focused helpers - Anti-patterns list extended Addition to languages/elisp/claude/rules/elisp-testing.md: - Interactive vs Internal split pattern: cj/foo wraps cj/--foo; test the internal directly, skip UI mocks Source: ~/.emacs.d/ai-prompts/quality-engineer.org (personal reference, kept as an extended prompt separate from these rules).
Diffstat (limited to 'languages')
-rw-r--r--languages/elisp/claude/rules/elisp-testing.md26
1 files changed, 26 insertions, 0 deletions
diff --git a/languages/elisp/claude/rules/elisp-testing.md b/languages/elisp/claude/rules/elisp-testing.md
index 3883902..b5def78 100644
--- a/languages/elisp/claude/rules/elisp-testing.md
+++ b/languages/elisp/claude/rules/elisp-testing.md
@@ -43,6 +43,32 @@ Write the failing test first. A failing test proves you understand the change. A
For untested code, write a **characterization test** that captures current behavior before you change anything. It becomes the safety net for the refactor.
+## Interactive vs Internal — Split for Testability
+
+When a function mixes business logic with user interaction, split it:
+
+- **Internal** (`cj/--foo`) — pure logic. All parameters explicit. No prompts,
+ no UI. Deterministic and trivially testable.
+- **Interactive wrapper** (`cj/foo`) — thin layer that reads user input and
+ delegates to the internal.
+
+```elisp
+(defun cj/--move-buffer-and-file (dir &optional ok-if-exists)
+ "Move the current buffer's file into DIR. Overwrite if OK-IF-EXISTS."
+ ...)
+
+(defun cj/move-buffer-and-file ()
+ "Interactive wrapper: prompt for DIR, delegate."
+ (interactive)
+ (let ((dir (read-directory-name "Move to: ")))
+ (cj/--move-buffer-and-file dir)))
+```
+
+Test the internal directly with parameter values — no `cl-letf` on
+`read-directory-name`, `yes-or-no-p`, etc. The wrapper gets a smoke test or
+nothing — Emacs already tests its own prompts. The internal also becomes
+reusable by other Elisp code without triggering UI.
+
## Mocking
Mock at boundaries: