
Tdd
Apply TDD-friendly module design—small interfaces, injected dependencies, and pure results—while writing or refactoring code with your coding agent.
Install
npx skills add https://github.com/mattpocock/ai-engineer-workshop-2026-project --skill tddWhat is this skill?
- Frames modules using the deep-vs-shallow interface mental model from A Philosophy of Software Design
- Three testability rules: inject dependencies, return results instead of hidden side effects, and shrink public surface a
- TypeScript-oriented before/after examples for payment processing and discount calculation
- Checklist questions to reduce methods, simplify parameters, and hide complexity inside modules
- Pairs naturally with red-green-refactor workflows when an agent is generating implementation code
Adoption & trust: 25 installs on skills.sh; 146 GitHub stars; 3/3 security scanners passed (skills.sh audits).
Recommended Skills
Journey fit
TDD and testability land on the Ship shelf because the skill’s payoff is reliable tests and QA-ready interfaces, even though you apply it while building features. Testing is the canonical subphase: the guidance exists to make unit and integration tests straightforward before you merge or release.
Common Questions / FAQ
Is Tdd safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Tdd
# Deep Modules From "A Philosophy of Software Design": **Deep module** = small interface + lots of implementation ``` ┌─────────────────────┐ │ Small Interface │ ← Few methods, simple params ├─────────────────────┤ │ │ │ │ │ Deep Implementation│ ← Complex logic hidden │ │ │ │ └─────────────────────┘ ``` **Shallow module** = large interface + little implementation (avoid) ``` ┌─────────────────────────────────┐ │ Large Interface │ ← Many methods, complex params ├─────────────────────────────────┤ │ Thin Implementation │ ← Just passes through └─────────────────────────────────┘ ``` When designing interfaces, ask: - Can I reduce the number of methods? - Can I simplify the parameters? - Can I hide more complexity inside? # Interface Design for Testability Good interfaces make testing natural: 1. **Accept dependencies, don't create them** ```typescript // Testable function processOrder(order, paymentGateway) {} // Hard to test function processOrder(order) { const gateway = new StripeGateway(); } ``` 2. **Return results, don't produce side effects** ```typescript // Testable function calculateDiscount(cart): Discount {} // Hard to test function applyDiscount(cart): void { cart.total -= discount; } ``` 3. **Small surface area** - Fewer methods = fewer tests needed - Fewer params = simpler test setup # When to Mock Mock at **system boundaries** only: - External APIs (payment, email, etc.) - Databases (sometimes - prefer test DB) - Time/randomness - File system (sometimes) Don't mock: - Your own classes/modules - Internal collaborators - Anything you control ## Designing for Mockability At system boundaries, design interfaces that are easy to mock: **1. Use dependency injection** Pass external dependencies in rather than creating them internally: ```typescript // Easy to mock function processPayment(order, paymentClient) { return paymentClient.charge(order.total); } // Hard to mock function processPayment(order) { const client = new StripeClient(process.env.STRIPE_KEY); return client.charge(order.total); } ``` **2. Prefer SDK-style interfaces over generic fetchers** Create specific functions for each external operation instead of one generic function with conditional logic: ```typescript // GOOD: Each function is independently mockable const api = { getUser: (id) => fetch(`/users/${id}`), getOrders: (userId) => fetch(`/users/${userId}/orders`), createOrder: (data) => fetch('/orders', { method: 'POST', body: data }), }; // BAD: Mocking requires conditional logic inside the mock const api = { fetch: (endpoint, options) => fetch(endpoint, options), }; ``` The SDK approach means: - Each mock returns one specific shape - No conditional logic in test setup - Easier to see which endpoints a test exercises - Type safety per endpoint # Refactor Candidates After TDD cycle, look for: - **Duplication** → Extract function/class - **Long methods** → Break into private helpers (keep tests on public interface) - **Shallow modules** → Combine or deepen - **Feature envy** → Move logic to where data lives - **Primitive obsession** → Introduce value objects - **Existing code** the new code reveals as problematic --- name: tdd description: Test-driven development with red-green-refactor loop. Use when user wants to build features or fix bugs using TDD, mentions "red-green-refactor", wants integration tests, or asks for test-first development. --- # Test-Driven Development ## Philosophy **Core principle**: Tests should verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't. **Good tests** are integration-style: they exercise real code paths through public APIs. They describe _what_ the system does, not _how_ it does it. A good test reads like a specification - "user can c