From silver-bullet
Enforces dependency injection, pure functions, and seam-based design so every component can be tested in isolation. Use during planning or review of non-trivial changes.
How this skill is triggered — by the user, by Claude, or both
Slash command
/silver-bullet:testabilityThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Every design, plan, and implementation MUST produce code that is easy to test in isolation. If it's hard to test, the design is wrong — not the test.
Every design, plan, and implementation MUST produce code that is easy to test in isolation. If it's hard to test, the design is wrong — not the test.
Why this matters: Untestable code is unverifiable code. When a simple test cannot be written for a component, it's because the component has too many hidden dependencies, side effects, or tightly coupled concerns. Testability is a direct measure of design quality.
When to invoke: During PLANNING (after /silver:context, before /silver:plan) and during REVIEW (as part of code review criteria). This skill applies to both new code and modifications to existing code.
Every external dependency MUST be injectable — never hard-coded:
| Hard-wired (untestable) | Injected (testable) |
|---|---|
import { db } from './database' at module level | function getUser(db, userId) |
new PaymentProvider() inside business logic | function processPayment(provider, order) |
Date.now() called directly | function isExpired(clock, token) |
fetch('https://api.example.com') inline | function getPrice(httpClient, productId) |
The rule: If a function calls something external (DB, API, clock, filesystem, random), that thing MUST be a parameter or constructor argument — not an import or global.
Exception: Pure utility libraries (lodash, date-fns) that are deterministic and side-effect-free can be imported directly.
Core business logic MUST be pure functions where possible:
Impure shell, pure core: Push side effects to the edges. The core logic is pure and easy to test; the thin outer layer handles I/O.
WRONG: function calculateDiscount(userId) {
const user = db.getUser(userId); // side effect: DB read
const tier = user.tier;
sendAnalytics('discount_calc', tier); // side effect: network
return tier === 'gold' ? 0.2 : 0.1;
}
RIGHT: function calculateDiscount(tier) {
return tier === 'gold' ? 0.2 : 0.1; // pure: input → output
}
Every module MUST have clear seams — points where behavior can be substituted for testing:
| Seam type | Example |
|---|---|
| Constructor injection | new OrderService(mockDb, mockMailer) |
| Function parameters | processOrder(order, { sendEmail, chargeCard }) |
| Configuration | { baseUrl: 'http://test-server' } |
| Interface/protocol | implements PaymentGateway → swap real for mock |
| Environment variables | process.env.API_URL = 'http://localhost:3001' |
Test: Can you write a test for this component without starting a database, calling an API, or waiting for a timer? If not, add seams.
Every operation MUST produce observable results that tests can verify:
| Unobservable (untestable) | Observable (testable) |
|---|---|
| Writes to a private field with no getter | Returns a result or updates observable state |
| Logs to console as the only output | Returns success/failure + logs (test the return value) |
| Fires and forgets an async operation | Returns a promise/future that resolves when done |
| Mutates a global in another module | Returns the new state, or emits an event |
The rule: If the only way to verify a function worked is to check a log file or database table, the function needs to return a result.
Tests MUST produce the same result every time. Eliminate sources of non-determinism:
| Source of non-determinism | Fix |
|---|---|
Current time (Date.now()) | Inject a clock; use fixed timestamps in tests |
Random numbers (Math.random()) | Inject a random source; use seeded generators in tests |
| UUIDs | Inject a generator; use predictable IDs in tests |
| Network responses | Mock HTTP clients; use recorded responses |
| File system state | Use in-memory filesystem or temp directories |
| Database auto-increment IDs | Don't assert on IDs; use deterministic test data |
| Parallel execution order | Design for order-independence |
Every testable unit MUST have a small, focused interface:
Test: Count the mocks in the test. If more than 3 mocks are needed, the component is coupled to too many things — split it.
Every test MUST run independently:
Test: Run a single test in isolation. Does it pass? Run all tests in random order. Do they all pass? If not, fix the shared state.
Before finalizing any design or plan, run the Testability Checklist:
If any item fails: redesign before proceeding to implementation.
As you write code:
Verify these as part of every code review:
If existing code violates these rules:
| Pattern | Problem | Fix |
|---|---|---|
Hard-coded new Database() | Can't test without real DB | Inject via constructor/parameter |
Date.now() in business logic | Tests flake across time zones | Inject a clock |
| Global singletons | Tests pollute each other | Inject instances, no globals |
| Test setup > 20 lines | Component too complex | Split component, simplify interface |
| Sleep in tests | Flaky, slow | Use deterministic waits (event, callback) |
| Testing implementation details | Tests break on refactor | Test behavior, not internals |
| Excuse | Reality |
|---|---|
| "It's hard to test because it's complex" | It's complex because it's poorly designed. Simplify. |
| "Mocking is too much work" | If mocking is hard, the dependencies are too coupled. |
| "Integration tests cover it" | Integration tests are slow and don't isolate failures. |
| "This doesn't need tests" | If it's not tested, it's not verified. |
| "I'll add tests later" | Untested code is untestable code. Design for tests now. |
| "The test would just mirror the implementation" | Then the implementation has no logic worth testing — or your test is wrong. |
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub alo-exp/silver-bullet --plugin silver-bullet