Marketplace tdd-workflow
Test-Driven Development methodology for Node.js/TypeScript projects.
install
source · Clone the upstream repo
git clone https://github.com/aiskillstore/marketplace
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/aiskillstore/marketplace "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/doubleslashse/tdd-workflow" ~/.claude/skills/aiskillstore-marketplace-tdd-workflow && rm -rf "$T"
manifest:
skills/doubleslashse/tdd-workflow/SKILL.mdsource content
TDD Workflow Skill
Overview
Test-Driven Development methodology for Node.js/TypeScript projects.
The RED-GREEN-REFACTOR Cycle
RED Phase: Design Failing Tests
Write tests BEFORE implementation:
- Identify Behavior: What should the code do?
- Design Test Cases: Cover all scenarios
- Write Tests: Use AAA pattern
- Run Tests: Confirm they FAIL
- Verify Failure: Tests fail for the RIGHT reason
GREEN Phase: Minimal Implementation
Make tests pass with minimal code:
- Focus: One failing test at a time
- Implement: Just enough to pass
- Verify: Run tests, confirm GREEN
- Iterate: Next failing test
- Complete: All tests passing
REFACTOR Phase: Improve Design
Improve code while keeping tests green:
- Review: Identify code smells
- Plan: Choose refactoring
- Apply: Make the change
- Verify: Tests still GREEN
- Repeat: Until quality gates met
AAA Pattern
describe('Calculator', () => { it('should add two numbers correctly', () => { // Arrange - Set up test conditions const calculator = createCalculator(); // Act - Execute the behavior const result = calculator.add(2, 3); // Assert - Verify the outcome expect(result).toBe(5); }); });
Test Naming Convention
Format:
should {expectedBehavior} when {scenario}
Examples:
it('should return empty array when input is empty', ...); it('should throw ValidationError when email is invalid', ...); it('should emit event when state changes', ...);
Test Categories
Unit Tests
- Test pure functions and logic
- No I/O, no side effects
- Fast execution
- High isolation
describe('validateEmail', () => { it('should return true for valid email', () => { expect(validateEmail('user@example.com')).toBe(true); }); });
Integration Tests
- Test module boundaries
- Include I/O operations
- Test with real (or fake) dependencies
describe('UserService', () => { it('should persist user to database', async () => { const db = createTestDatabase(); const service = createUserService({ db }); await service.createUser({ email: 'test@example.com' }); const user = await db.users.findFirst(); expect(user.email).toBe('test@example.com'); }); });
Contract Tests
- Verify API contracts
- Type safety at boundaries
- Response shape validation
describe('API Contract', () => { it('should return user with expected shape', async () => { const response = await api.getUser('1'); expect(response).toMatchObject({ id: expect.any(String), email: expect.any(String), createdAt: expect.any(Date), }); }); });
Test Doubles
Stub
Returns canned data:
const stubApi = { getUser: () => Promise.resolve({ id: '1', name: 'Test' }), };
Mock
Verifies interactions:
const mockLogger = { info: jest.fn(), error: jest.fn(), }; // Later: expect(mockLogger.info).toHaveBeenCalledWith('message');
Fake
Working implementation:
const createFakeDatabase = () => { const store = new Map(); return { save: (entity) => store.set(entity.id, entity), findById: (id) => store.get(id), }; };
Spy
Records calls:
const spy = jest.spyOn(service, 'notify'); await service.process(); expect(spy).toHaveBeenCalledTimes(1);
Test Organization
src/ services/ user-service.ts user-service.test.ts # Co-located unit tests api/ handlers.ts handlers.test.ts tests/ integration/ # Integration tests user-flow.test.ts fixtures/ # Shared test data users.ts helpers/ # Test utilities test-context.ts
Anti-Patterns
Testing Implementation Details
// Bad - testing internal state expect(service._cache.size).toBe(1); // Good - testing behavior expect(service.getCachedValue('key')).toBe('value');
Overly Specific Assertions
// Bad - brittle expect(result).toEqual({ id: '123', name: 'Test', createdAt: new Date('2024-01-01'), updatedAt: new Date('2024-01-01'), }); // Good - flexible expect(result).toMatchObject({ id: expect.any(String), name: 'Test', });
Test Interdependence
// Bad - tests depend on order let user; it('should create user', () => { user = createUser(); }); it('should update user', () => { updateUser(user); }); // Depends on previous // Good - independent tests it('should update user', () => { const user = createUser(); updateUser(user); });