Introduction
Roy Osherove's "The Art of Unit Testing" provides comprehensive guidance on writing effective unit tests. This article explores key concepts and practical examples from the book.
Test Structure: The AAA Pattern
Osherove emphasizes the Arrange-Act-Assert pattern for clear test structure:
describe('UserService', () => {
test('should create user with valid data', () => {
// Arrange
const userData = {
name: 'John Doe',
email: 'john@example.com'
};
const userService = new UserService();
// Act
const user = userService.createUser(userData);
// Assert
expect(user).toHaveProperty('id');
expect(user.name).toBe(userData.name);
expect(user.email).toBe(userData.email);
});
});
Test Naming Conventions
Osherove recommends the following naming pattern:
test('should [expected behavior] when [condition]', () => {
// Test implementation
});
// Examples:
test('should return true when user is admin', () => {
// Test implementation
});
test('should throw error when email is invalid', () => {
// Test implementation
});
Test Categories
- Happy Path Tests
- Test normal operation
- Verify expected outcomes
- Edge Cases
- Test boundary conditions
- Verify handling of edge values
- Error Cases
- Test error handling
- Verify error messages
Example: Comprehensive Test Suite
describe('Calculator', () => {
describe('add', () => {
test('should return sum of two positive numbers', () => {
const calculator = new Calculator();
expect(calculator.add(2, 3)).toBe(5);
});
test('should handle negative numbers', () => {
const calculator = new Calculator();
expect(calculator.add(-2, 3)).toBe(1);
});
test('should handle zero', () => {
const calculator = new Calculator();
expect(calculator.add(0, 5)).toBe(5);
});
});
describe('divide', () => {
test('should return quotient of two numbers', () => {
const calculator = new Calculator();
expect(calculator.divide(6, 2)).toBe(3);
});
test('should throw error when dividing by zero', () => {
const calculator = new Calculator();
expect(() => calculator.divide(5, 0))
.toThrow('Division by zero');
});
});
});
Best Practices
- Keep tests focused and atomic
- Use meaningful test names
- Follow the AAA pattern
- Test one concept per test
- Use appropriate assertions
Conclusion
Roy Osherove's approach to unit testing emphasizes clarity, maintainability, and effectiveness. By following these principles, we can create more reliable and maintainable tests.
"The act of writing a unit test is more an act of design than of verification. It is also more an act of documentation than of verification." - Roy Osherove
Member discussion: