
tdd-workflow
Guide TDD Red-Green-Refactor cycle for TypeScript development. Use when implementing new features, f
by ar4rpon|Open Source
TDD Workflow Guide
Overview
This skill guides the Test-Driven Development cycle: Red → Green → Refactor.
TDD Cycle
1. Red Phase (Write Failing Test)
Before writing any implementation:
- Understand the requirement - What behavior needs to be implemented?
- Write the test first - Create a test that describes the expected behavior
- Run the test - Confirm it fails for the right reason
// Example: Testing a new function
describe('calculateTotal', () => {
it('should return sum of item prices', () => {
const items = [{ price: 100 }, { price: 200 }];
expect(calculateTotal(items)).toBe(300);
});
});
Run test to confirm failure:
pnpm test
2. Green Phase (Make It Pass)
Write the minimum code to make the test pass:
- Implement only what's needed - No extra features
- Keep it simple - Don't optimize yet
- Run the test - Confirm it passes
// Minimal implementation
function calculateTotal(items: { price: number }[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
3. Refactor Phase (Improve Code)
With passing tests as safety net:
- Remove duplication
- Improve naming
- Extract functions/modules if needed
- Run tests after each change - Ensure nothing breaks
Project-Specific Patterns
Backend (NestJS + Vitest)
// apps/backend/src/*.spec.ts
import { Test } from '@nestjs/testing';
import { AppService } from './app.service';
describe('AppService', () => {
let service: AppService;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [AppService],
}).compile();
service = module.get(AppService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
Frontend (React + Vitest + RTL)
// apps/frontend/src/*.test.tsx
import { render, screen } from '@testing-library/react';
import { Component } from './Component';
describe('Component', () => {
it('should render correctly', () => {
render(<Component />);
expect(screen.getByRole('button')).toBeInTheDocument();
});
});
With MSW (API Mocking)
import { server } from '@/test/mocks/server';
import { http, HttpResponse } from 'msw';
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
it('should fetch and display data', async () => {
server.use(http.get('/api/users', () => HttpResponse.json([{ id: 1, name: 'Test' }])));
// Test implementation
});
Commands
# Run all tests
pnpm test
# Watch mode (recommended during TDD)
pnpm test:watch
# Run specific test file
pnpm test -- path/to/file.spec.ts
# Coverage report
pnpm test:coverage
Checklist
Copy and track progress:
- [ ] Understand the requirement
- [ ] Write failing test (Red)
- [ ] Confirm test fails for right reason
- [ ] Write minimal implementation (Green)
- [ ] Confirm test passes
- [ ] Refactor if needed
- [ ] Confirm tests still pass
- [ ] Repeat for next requirement
Anti-Patterns to Avoid
- Writing implementation before test - Always test first
- Writing too many tests at once - One test at a time
- Making test pass with hardcoded values - Implement real logic
- Skipping refactor phase - Technical debt accumulates
- Testing implementation details - Test behavior, not internals