testability-canary
Testability and design decoupling canary — checks for tight coupling, lack of Dependency Injection (DI), hardcoded constructors, Single Responsibility Principle (SRP) violations, and mockability gaps. Triggers on keywords: "/testability-canary", "testability-canary", "testability
Install
npx skills add https://github.com/TheColliery/CoalMine/tree/main/skills/testability-canary
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install hetcreep-coalmine@llmmart
git clone https://github.com/TheColliery/CoalMine.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole hetcreep/coalmine collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Testability Canary (Decoupling & Mockability Audit)
Audit code to ensure it is decoupled, modular, and easy to cover with automated tests.
Auditing Categories
- Hardcoded Constructors — instantiating deps inside classes (
new DatabaseClient()) instead of injecting via constructor/factory (prevents mocking). - SRP Violations — a class or method doing too many distinct duties (e.g. a service that also parses JSON and formats UI).
- Static Dependencies — reliance on global static methods or Singletons that make test isolation impossible.
- Time & Environment Coupling — direct
DateTime.Now,fs, orprocess.envcalls without an abstraction layer (fragile time/path-sensitive tests). - Private Logic Gaps — complex business logic hidden in private methods that can't be tested directly (extract to testable helpers).
Per-stack patterns and the mock-strategy vocabulary: read references/checks.md before scanning.
Fix mode (choice-gated)
In Agent Context, after the report, present via ask_question:
- Apply safe refactoring: extract hardcoded initializations into constructor params (DI) + add interface definitions. Each fix: checkpoint (git stash/commit in a git repo; else copy the file aside — never assume git) → apply → build + tests → auto-revert if newly red.
- Let me pick: user selects specific refactoring moves.
- Report only: exit unchanged.
Grants & denials (CLASSIFY-BLOCK)
| class | step it powers | grant | on denial |
|---|---|---|---|
| read | scan coupling/DI surfaces for the categories above | Read·Grep·Glob |
refuse that file, name it — never a clean bill |
| write | Fix mode's safe-refactor apply, incl. checkpoint → build+tests → auto-revert if newly red | Edit·Bash (checkpoint/build/revert need exec) |
report the fix as NOT applied AND the checkpoint/revert as NOT available, never claim done |
Output
| file:line | coupling point | severity | finding | mock strategy |
Severity: CRITICAL (un-mockable external write/network call) · HIGH (SRP violation blocking unit testing) · MEDIUM (time/env coupling) · LOW (minor static dependency)
Files (coalmine)
-
references
-
checks.md 2.3 KB
<!-- coalmine: verified 2026-06-12 · revalidate 90d · definition file for testability-canary --> # Testability canary — concrete detection procedures ## 1. Hardcoded constructors (no seam to mock) - Grep `new ` inside constructors/methods for I/O classes: `new HttpClient(`, `new SqlConnection(`, `new SmtpClient(`, `new S3Client(`, `new PrismaClient(` etc. - Right shape: dependency arrives via constructor param / factory / DI container registration. The class should depend on an interface/abstract type where one exists. - Flag only I/O or stateful deps — `new List<>()`/value objects are fine. ## 2. SRP violations blocking unit tests - One class that parses + computes + persists + formats: count distinct reasons to change. >2 = flag with the split suggestion. - Heuristic greps: a "Service" importing both an HTTP framework and a DB driver; methods >50 lines mixing I/O with branching logic. ## 3. Static / singleton dependencies | Stack | Patterns | |---|---| | C# | `static` mutable fields · `Foo.Instance` · `ServiceLocator` · static `HttpClient` used directly in logic | | TS/JS | module-level mutable singletons imported everywhere · `export const db = new Client()` consumed deep in logic | | Python | module-global clients (`requests.Session()` at import time) · singletons via module state | | Java/Kotlin | `getInstance()` chains · static utility classes wrapping I/O | - Fix shape: pass the instance in; keep module-level only for pure/stateless helpers. ## 4. Time & environment coupling - Direct calls inside business logic: `DateTime.Now`/`UtcNow` · `Date.now()`/`new Date()` · `time.time()`/`datetime.now()` · `process.env`/`os.environ` · `fs`/file paths. - Fix shape: inject a clock (`IClock`, `() => Date`), read env/config once at the boundary and pass values down. - Only flag where behavior depends on the value (scheduling, expiry, paths) — timestamps on log lines are fine. ## 5. Private logic gaps - Complex branching (cyclomatic >5) inside private methods with no public seam: recommend extracting a pure function/module with direct unit tests. - Do NOT recommend reflection or exposing privates — extraction only. ## Mock strategy column (output) For each finding name the seam: constructor injection · interface extract · clock injection · boundary param · pure-function extract.
-
-
skill-meta.json 183 B
{ "lightIntent": "Spot coupling check, key classes only", "standardIntent": "Balanced decoupling audit, multi-category", "heavyIntent": "Full 5-category audit + adversarial verify" } -
SKILL.md 2.7 KB
--- name: testability-canary description: >- Testability and design decoupling canary — checks for tight coupling, lack of Dependency Injection (DI), hardcoded constructors, Single Responsibility Principle (SRP) violations, and mockability gaps. Triggers on keywords: "/testability-canary", "testability-canary", "testability audit", "decoupling". Use when refactoring coupling, introducing DI, or making code unit-testable. --- # Testability Canary (Decoupling & Mockability Audit) <!-- SHARED:LANGUAGE_HEADER --> Audit code to ensure it is decoupled, modular, and easy to cover with automated tests. ## Auditing Categories 1. **Hardcoded Constructors** — instantiating deps inside classes (`new DatabaseClient()`) instead of injecting via constructor/factory (prevents mocking). 2. **SRP Violations** — a class or method doing too many distinct duties (e.g. a service that also parses JSON and formats UI). 3. **Static Dependencies** — reliance on global static methods or Singletons that make test isolation impossible. 4. **Time & Environment Coupling** — direct `DateTime.Now`, `fs`, or `process.env` calls without an abstraction layer (fragile time/path-sensitive tests). 5. **Private Logic Gaps** — complex business logic hidden in private methods that can't be tested directly (extract to testable helpers). Per-stack patterns and the mock-strategy vocabulary: read `references/checks.md` before scanning. ## Fix mode (choice-gated) In Agent Context, after the report, present via `ask_question`: - **Apply safe refactoring:** extract hardcoded initializations into constructor params (DI) + add interface definitions. Each fix: checkpoint (git stash/commit in a git repo; else copy the file aside — never assume git) → apply → build + tests → auto-revert if newly red. - **Let me pick:** user selects specific refactoring moves. - **Report only:** exit unchanged. ## Grants & denials (CLASSIFY-BLOCK) | class | step it powers | grant | on denial | |---|---|---|---| | read | scan coupling/DI surfaces for the categories above | `Read`·`Grep`·`Glob` | refuse that file, name it — never a clean bill | | write | Fix mode's safe-refactor apply, incl. checkpoint → build+tests → auto-revert if newly red | `Edit`·`Bash` (checkpoint/build/revert need exec) | report the fix as NOT applied AND the checkpoint/revert as NOT available, never claim done | <!-- SHARED:CLASSIFY_BLOCK --> ## Output `| file:line | coupling point | severity | finding | mock strategy |` Severity: CRITICAL (un-mockable external write/network call) · HIGH (SRP violation blocking unit testing) · MEDIUM (time/env coupling) · LOW (minor static dependency) <!-- SHARED:REPORTING_FOOTER --> <!-- SHARED:ORCHESTRATION --> <!-- SHARED:ESCALATION_FOOTER -->
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.