TDD (Test-Driven Development) Skill
Guide Claude to follow strict Test-Driven Development practices when implementing new features or fixing bugs. Ensures tests are written BEFORE implementation code.
When to Use
-
Implementing new features
-
Adding new methods to existing services/repositories
-
Fixing bugs (write failing test first)
-
Refactoring existing code
Core TDD Cycle
RED → GREEN → REFACTOR ↓ ↓ ↓ Fail Pass Improve
- RED - Write Failing Test
-
Write test that describes desired behavior
-
Test MUST fail (compilation error or assertion failure)
-
Verify test fails for the right reason
- GREEN - Make Test Pass
-
Write minimal code to make test pass
-
Don't worry about perfect code yet
-
Just make it work
- REFACTOR - Improve Code
-
Clean up implementation
-
Remove duplication
-
Improve naming
-
Tests still pass
Critical Rules
Rule 1: ALWAYS Identify Entity Under Test
Before writing any test, Claude MUST:
-
Ask the user if entity under test is ambiguous
-
State explicitly which entity is being tested
-
Use correct naming in test file and test functions
See examples/dialogue-examples.md for examples of clear vs ambiguous entity identification.
Rule 2: Test File Location
Place test files next to the code being tested:
internal/service/ ├── transaction_service.go └── transaction_service_test.go ← Test file here
pkg/ofx/ ├── parser.go └── parser_test.go ← Test file here
Rule 3: Test Naming Convention
Pattern: Test<EntityName><MethodName><Scenario>
See data/naming-conventions.md for detailed naming guidelines and examples.
// ✅ GOOD - Clear entity, method, and scenario func TestTransactionService_ImportFromOFX_WithDuplicateFITID(t *testing.T) func TestOFXParser_Parse_WithInvalidXML(t *testing.T) func TestBudgetRepository_Create_WithNullAmount(t *testing.T)
// ❌ BAD - Missing entity or scenario func TestImport(t *testing.T) func TestValidData(t *testing.T)
Rule 4: Test Main Cases (Not Everything)
Focus on:
-
Happy path (1 test)
-
Common errors (2-3 tests)
-
Edge cases (1-2 tests)
-
Business rules (as many as needed)
Don't test:
-
Language features (e.g., "does append work?")
-
Third-party library internals
-
Trivial getters/setters
-
Every possible combination
See data/coverage-guidelines.md for what to test and what to skip.
Test Structure (AAA Pattern)
Use templates from templates/test-structure.md :
func TestTransactionService_ImportFromOFX_Success(t *testing.T) { // ARRANGE - Setup test data and dependencies mockRepo := &mockTransactionRepository{} parser := ofx.NewParser() service := NewTransactionService(mockRepo, parser)
ofxData := []byte(`<OFX>...</OFX>`)
accountID := uuid.New()
// ACT - Execute the method being tested
result, err := service.ImportFromOFX(context.Background(), ofxData, accountID)
// ASSERT - Verify expectations
require.NoError(t, err)
assert.Equal(t, 5, result.Inserted)
assert.Equal(t, 0, result.Skipped)
}
TDD Workflow Examples
See examples/feature-implementation.md for complete examples:
Example 1: New Feature
-
RED - Write failing test for BudgetService.CalculateEffectiveAmount()
-
GREEN - Implement minimal code to pass
-
REFACTOR - Add more test cases and improve
Example 2: Bug Fix
-
RED - Reproduce bug with failing test
-
GREEN - Fix the bug to make test pass
-
REFACTOR - Verify no other bugs introduced
Example 3: Multiple Scenarios
Test 4 main cases for each method:
-
Happy path
-
Error handling
-
Boundary conditions
-
Business rules
Test Coverage Guidelines
From data/coverage-guidelines.md :
What to Test ✅
-
Repository: CRUD, constraints, complex queries
-
Service: Business logic, validation, error handling
-
Handler: Request/response, status codes, auth
What NOT to Test ❌
-
Language features
-
Framework internals
-
Database engine behavior
-
Third-party libraries
-
Trivial code
Mocking Guidelines
When to Mock
-
External dependencies (HTTP clients, databases)
-
Other services in service layer
-
Slow operations
-
Non-deterministic behavior (time, random)
How to Mock
See templates/test-structure.md for mock templates:
// Define interface in domain type TransactionRepository interface { BulkInsert(ctx context.Context, txs []*Transaction) error }
// Create mock in test file type mockTransactionRepository struct { mock.Mock }
func (m *mockTransactionRepository) BulkInsert(ctx context.Context, txs []*Transaction) error { args := m.Called(ctx, txs) return args.Error(0) }
Claude's TDD Checklist
Before writing implementation code, Claude MUST:
-
Identify entity under test explicitly
-
Create/open correct test file (*_test.go )
-
Write test with clear AAA structure
-
Run test and verify it FAILS
-
State why test fails (compilation or assertion)
-
Ask user if entity or scenario is ambiguous
After test fails, Claude can:
-
Implement minimal code to pass test
-
Run test and verify it PASSES
-
Refactor if needed
-
Add more test cases for edge cases
Common Mistakes to Avoid
❌ Writing Implementation First
WRONG: "I'll implement ImportFromOFX, then test it" RIGHT: "I'll write a test for ImportFromOFX first, then implement"
❌ Testing Multiple Things in One Test
One scenario per test function.
❌ Unclear Entity Under Test
Always specify which entity (Service, Repository, Handler) is being tested.
❌ Testing Implementation Details
Test public behavior, not internal variables or private methods.
TDD Dialog Examples
See examples/dialogue-examples.md for:
-
How to handle clear entity requests
-
How to ask for clarification when ambiguous
-
How to propose multiple test scenarios
-
How to stop before writing implementation without tests
Summary
TDD in 3 Steps:
-
🔴 RED - Write failing test (describe what you want)
-
🟢 GREEN - Make test pass (implement minimal code)
-
🔵 REFACTOR - Improve code (keep tests passing)
Always remember:
-
Identify entity under test explicitly
-
Write test BEFORE implementation
-
Test main cases (not every possibility)
-
Use clear naming conventions
-
Ask when unsure
Golden Rule: If you're writing implementation code without a failing test, STOP and write the test first.
Reference Files
-
Examples: See examples/feature-implementation.md and examples/dialogue-examples.md
-
Templates: See templates/test-structure.md for test patterns and mocks
-
Naming: See data/naming-conventions.md for naming guidelines
-
Coverage: See data/coverage-guidelines.md for what to test