pragmatic-tdd
Use when implementing features with test-driven development, writing tests before code, building domain-rich business logic, or following hexagonal architecture
What this skill does
# Pragmatic TDD Skill
You are a Test-Driven Development expert guiding developers through pragmatic TDD based on Hexagonal Architecture and Domain-Driven Design.
## Philosophy
This skill follows a pragmatic approach to TDD that:
- **Tests behavior, not implementation** - Focuses on what the code does, not how
- **Minimizes test brittleness** - Tests survive refactoring
- **Tests real flows** - Not isolated mock-based illusions
- **Follows Hexagonal Architecture** - Clear separation between domain and infrastructure
## Core Principles
### 1. Test via Primary Ports
Test the system through its public API/ports, not internal details.
**Why?** If you can refactor the entire internal structure without tests breaking, you're testing the right thing.
```typescript
// ❌ AVOID: Testing internal details
test('UserValidator.validateEmail should check format', () => {
const validator = new UserValidator();
expect(validator.validateEmail('[email protected]')).toBe(true);
});
// ✅ GOOD: Test via primary port
test('User registration should reject invalid email', async () => {
const service = new UserRegistrationService(adapters);
await expect(
service.registerUser({ email: 'invalid-email', ...})
).rejects.toThrow('Invalid email format');
});
```
### 2. Mock Only at Adapter Boundaries
Mock only external dependencies (database, HTTP, filesystem), never internal domain logic.
**Why?** Internal mocks test a fiction. External mocks control the uncontrollable.
```typescript
// ❌ AVOID: Mocking internal domain logic
const mockValidator = {
validateEmail: jest.fn().mockReturnValue(true)
};
const service = new UserService(mockValidator);
// ✅ GOOD: Mock only adapters
const mockRepository = {
save: jest.fn(),
findByEmail: jest.fn()
};
const service = new UserRegistrationService(mockRepository, new EmailService());
// Domain logic and validators run real code
```
### 3. Verify Business Flows
Tests should prove that business rules actually work, not that code executes.
**Why?** Unit tests on isolated classes don't prove that logic works as a whole.
```typescript
// ❌ AVOID: Testing parts in isolation
test('CompetitorChecker returns true for competitor domain', () => {
const checker = new CompetitorChecker(['competitor.com']);
expect(checker.isCompetitor('[email protected]')).toBe(true);
});
// ✅ GOOD: Test the entire flow
test('Users from competitor domains should be flagged for review', async () => {
const service = new UserRegistrationService(adapters);
const result = await service.registerUser({
email: '[email protected]',
name: 'John Doe'
});
expect(result.status).toBe('PENDING_REVIEW');
expect(result.flagReason).toBe('COMPETITOR_DOMAIN');
expect(mockEmailService.sendAdminAlert).toHaveBeenCalled();
});
```
### 4. Accept That Tests Should Change with Behavior Changes
But not with internal structure refactoring.
**Why?** This doesn't violate the Open/Closed Principle - OCP applies to production code, not tests.
## Test-Driven Development Cycle
```
1. RED: Write test for behavior (via primary port)
└─> Test fails (function doesn't exist yet)
2. GREEN: Implement minimal domain logic
└─> Test passes
3. REFACTOR: Improve internal structure
└─> Tests remain green (they test behavior, not structure)
```
## Hexagonal Architecture Mapping
```
┌─────────────────────────────────────────┐
│ Primary Ports (TEST HERE) │
│ - UserRegistrationService │
│ - OrderProcessingService │
└─────────────┬───────────────────────────┘
│
┌─────────────▼───────────────────────────┐
│ Domain Layer (Real code in tests) │
│ - User, Order (Entities) │
│ - DomainValidators │
│ - Business Rules │
└─────────────┬───────────────────────────┘
│
┌─────────────▼───────────────────────────┐
│ Adapters (MOCK HERE) │
│ - UserRepository (DB) │
│ - EmailService (SMTP) │
│ - PaymentGateway (HTTP) │
└─────────────────────────────────────────┘
```
## Common Mistakes
### ❌ Mistake 1: Testing Implementation Details
**Problem:** Tests break with every refactoring
**Solution:** Test via public ports, not private methods
### ❌ Mistake 2: Mocking Everything
**Problem:** Tests pass but system doesn't work
**Solution:** Mock only adapters, run real domain logic
### ❌ Mistake 3: Too Many Low-Level Unit Tests
**Problem:** Hundreds of tests, no confidence in the whole
**Solution:** Balance with integration tests via primary ports
### ❌ Mistake 4: Testing "What the Code Does" Instead of "What It Should Do"
**Problem:** Tests-after document existing behavior, not requirements
**Solution:** Write test FIRST based on business requirements
## TDD Workflow
When you're asked to implement a feature using TDD:
1. **Understand the Requirement**
- What behavior needs to be implemented?
- What are the business rules?
- What are the edge cases?
2. **RED Phase**
- Write a test that describes the desired behavior
- Test via the primary port (public API)
- Run the test - it should FAIL
- If it passes, you're not testing new behavior
3. **GREEN Phase**
- Write the minimal code to make the test pass
- Don't over-engineer
- Focus on making it work, not perfect
4. **REFACTOR Phase**
- Clean up the implementation
- Extract domain objects if needed
- Improve naming and structure
- Tests should remain GREEN
5. **Repeat**
- Move to the next behavior
- Build incrementally
## When to Use This Approach
✅ **Use when:**
- You're building domain-rich business logic
- You want tests that survive refactoring
- You follow DDD or Hexagonal Architecture
- You need confidence that business flows actually work
❌ **Don't use when:**
- You're writing simple CRUD operations without business logic
- The project has no clear domain layer separation
- You need to test algorithmic correctness in isolation
## Example: Complete TDD Flow
### Requirement
"Users from competitor domains should be flagged for manual review"
### 1. RED: Write Test First
```typescript
describe('UserRegistrationService', () => {
let service: UserRegistrationService;
let mockUserRepo: MockUserRepository;
let mockEmailService: MockEmailService;
beforeEach(() => {
mockUserRepo = new MockUserRepository();
mockEmailService = new MockEmailService();
service = new UserRegistrationService(
mockUserRepo,
mockEmailService,
['competitor.com', 'rival.io']
);
});
test('should flag competitor domain users for review', async () => {
const userData = {
email: '[email protected]',
name: 'John Doe',
password: 'securePass123'
};
const result = await service.registerUser(userData);
expect(result.status).toBe('PENDING_REVIEW');
expect(result.flagReason).toBe('COMPETITOR_DOMAIN');
expect(result.user.isActive).toBe(false);
expect(mockEmailService.adminAlerts).toHaveLength(1);
});
});
```
### 2. GREEN: Implement
```typescript
class UserRegistrationService {
constructor(
private userRepo: UserRepository,
private emailService: EmailService,
private competitorDomains: string[]
) {}
async registerUser(data: UserRegistrationData): Promise<RegistrationResult> {
const domain = this.extractDomain(data.email);
const isCompetitor = this.competitorDomains.includes(domain);
const user = new User(
data.email,
data.name,
await this.hashPassword(data.password),
!isCompetitor,
isCompetitor ? 'COMPETITOR_DOMAIN' : undefined
);
await this.userRepo.save(user);
if (isCompetitor) {
await this.emailService.sendAdminAlert({
subject: 'Competitor Signup Detected',
body: `User ${data.email} from competitor domain attempted signup`
});
return { status: 'PENDING_REVIEW', flagReason: 'COMPETITOR_DORelated in Writing & Docs
jax-development
IncludedUse this skill when the user is writing, debugging, profiling, refactoring, reviewing, benchmarking, parallelising, exporting, or explaining JAX code, or when they mention JAX, jax.numpy, jit, grad, value_and_grad, vmap, scan, lax, random keys, pytrees, jax.Array, sharding, Mesh, PartitionSpec, NamedSharding, pmap, shard_map, Pallas, XLA, StableHLO, checkify, profiler, or the JAX repo. It helps turn NumPy or PyTorch-style code into pure functional JAX, fix tracer/control-flow/shape/PRNG bugs, remove recompiles and host-device syncs, choose transforms and sharding strategies, inspect jaxpr/lowering/IR, and benchmark compiled code correctly.
nature-article-writer
IncludedDrafts, rewrites, diagnostically critiques, and style-calibrates primary research manuscripts for Nature and Nature Portfolio journals. Use when the user wants a Nature-style title, summary paragraph or abstract, introduction, results, discussion, methods, figure legends, presubmission enquiry, cover letter, reviewer response, or when a scientific draft sounds generic, jargon-heavy, structurally weak, or AI-ish and needs precise, broad-reader-friendly prose without inventing data, analyses, or references. Best for primary research articles and letters rather than reviews or press releases unless explicitly adapting one.
deckrd
IncludedDocument-driven framework that derives requirements, specifications, implementation plans, and executable tasks from goals through structured AI dialogue. Use when user says "write requirements", "create spec", "plan implementation", "derive tasks", "structure this feature", "break down into tasks", or "document this module". Also use for reverse engineering existing code into docs (/deckrd rev). Do NOT use for direct code writing — use /deckrd-coder after tasks are generated. Do NOT use when the user only wants to run or fix existing code without planning.
clinical-decision-support
IncludedGenerate professional clinical decision support (CDS) documents for pharmaceutical and clinical research settings, including patient cohort analyses (biomarker-stratified with outcomes) and treatment recommendation reports (evidence-based guidelines with decision algorithms). Supports GRADE evidence grading, statistical analysis (hazard ratios, survival curves, waterfall plots), biomarker integration, and regulatory compliance. Outputs publication-ready LaTeX/PDF format optimized for drug development, clinical research, and evidence synthesis.
handling-sf-data
IncludedSalesforce data operations with 130-point scoring. Use this skill to create, update, delete, bulk import/export, generate test data, and clean up org records using sf CLI and anonymous Apex. TRIGGER when: user creates test data, performs bulk import/export, uses sf data CLI commands, needs data factory patterns for Apex tests, or needs to seed/clean records in a Salesforce org. DO NOT TRIGGER when: SOQL query writing only (use querying-soql), Apex test execution (use running-apex-tests), or metadata deployment (use deploying-metadata).
accelint-ac-to-playwright
IncludedConvert and validate acceptance criteria for Playwright test automation. Use when user asks to (1) review/evaluate/check if AC are ready for automation, (2) assess if AC can be converted as-is, (3) validate AC quality for Playwright, (4) turn AC into tests, (5) generate tests from acceptance criteria, (6) convert .md bullets or .feature Gherkin files to Playwright specs, (7) create test automation from requirements. Handles both bullet-style markdown and Gherkin syntax with JSON test plan generation and validation.