π§ͺ Test-Driven Development (TDD)
π Definitionβ
TDD (Test-Driven Development) is a development methodology where you write tests first, then write code to pass those tests. It follows the Red-Green-Refactor cycle, improving code quality, reducing bugs, and making refactoring safer. Unit tests verify individual functions or components independently.
π― Simple Analogyβ
Blueprint Firstβ
Traditional Development
1. Build house
2. Check after completion
3. Find problems β Big fixes needed
4. Cost increases
TDD
1. Draw blueprint (Write test)
2. Build per blueprint (Write code)
3. Verify (Run test)
4. Improve (Refactor)
5. Safe and accurate
βοΈ How It Worksβ
TDD Cycle (Red-Green-Refactor)β
π΄ Red (Fail)
ββ Write test β Fails (no code)
π’ Green (Pass)
ββ Write minimal code β Test passes
π΅ Refactor (Improve)
ββ Improve code β Test still passes
Repeat β Gradual improvement
π‘ Key Examplesβ
Basic TDD Exampleβ
// ========== 1. Red: Write test (fails) ==========
test('add function adds two numbers', () => {
expect(add(2, 3)).toBe(5);
});
// FAIL - add is not defined
// ========== 2. Green: Minimal code (passes) ==========
function add(a, b) {
return a + b;
}
// PASS β
// ========== 3. Refactor: Improve (if needed) ==========
// Code is simple, no improvement needed
Unit Test with Jestβ
// user.js
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
isValidEmail() {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(this.email);
}
}
// user.test.js
describe('User', () => {
let user;
beforeEach(() => {
user = new User('John', 'john@example.com');
});
test('should return true for valid email', () => {
expect(user.isValidEmail()).toBe(true);
});
test('should return false for invalid email', () => {
user.email = 'invalid-email';
expect(user.isValidEmail()).toBe(false);
});
});
Async Testingβ
test('async function returns data', async () => {
const data = await fetchData();
expect(data).toEqual({ name: 'John' });
});
// With error handling
test('async function throws error', async () => {
try {
await fetchData();
} catch (error) {
expect(error.message).toBe('Network error');
}
});
Mockingβ
// Mock function
const mockCallback = jest.fn(x => x * 2);
[1, 2, 3].forEach(mockCallback);
expect(mockCallback).toHaveBeenCalledTimes(3);
expect(mockCallback).toHaveBeenCalledWith(1);
// Mock module
jest.mock('axios');
test('fetchUser returns user data', async () => {
axios.get.mockResolvedValue({ data: { id: 1 } });
const user = await fetchUser(1);
expect(user).toEqual({ id: 1 });
});
React Component Testingβ
import { render, screen, fireEvent } from '@testing-library/react';
test('button calls onClick when clicked', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
π€ FAQβ
Q1. Benefits of TDD?β
A:
Pros:
1. Fewer bugs
2. Safe refactoring
3. Better code quality
4. Documentation
5. Confidence
Cons:
1. Initial time investment
2. Learning curve
3. Test maintenance
Conclusion: Long-term gain
Q2. What to Test?β
A:
// β
Should test
1. Business logic
2. Edge cases
3. Error handling
4. Public APIs
// β Don't need to test
1. External libraries (already tested)
2. Simple getters/setters
3. Private implementation details
Q3. Test Coverage Goal?β
A:
# Measure coverage
npm test -- --coverage
# Goal:
80%+ coverage // Realistic goal
100% coverage // Ideal but impractical
# Remember:
High coverage β Good tests
Meaningful tests matter!
Q4. Unit vs Integration Tests?β
A:
// Unit Test: Individual functions
test('add function adds numbers', () => {
expect(add(2, 3)).toBe(5);
});
// Integration Test: Multiple modules together
test('user registration flow', async () => {
const user = await createUser({ name: 'John' });
const dbUser = await db.users.findOne({ email: 'john@example.com' });
expect(dbUser).toBeDefined();
});
// Test Pyramid:
70% Unit tests
20% Integration tests
10% E2E tests
π¬ Summaryβ
TDD is the foundation of software quality:
- Red-Green-Refactor: Core TDD cycle
- Unit Tests: Fast and isolated
- Test First: Tests drive design
- Continuous Improvement: Safety net for refactoring
Tests are an investment in your future self! π§ͺβ¨