Skip to main content

πŸ§ͺ 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! πŸ§ͺ✨