20.1 Unit Testing Fundamentals
Overview
Unit testing is the practice of testing individual units of code (functions, methods, classes) in isolation to ensure they work correctly.
Why Unit Test?
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Benefits of Unit Testing ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā āāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāā ā
ā ā Catch Bugs ā ā Documentation ā ā Refactoring ā ā
ā ā Early ā ā by Example ā ā Confidence ā ā
ā āāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāā ā
ā ā
ā āāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāā ā
ā ā Faster ā ā Better Code ā ā Regression ā ā
ā ā Debugging ā ā Design ā ā Prevention ā ā
ā āāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāā ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
AAA Pattern
| Phase | Description | Example |
|---|
| Arrange | Set up test data and conditions | Create test objects, initialize state |
| Act | Execute the code being tested | Call the function with test data |
| Assert | Verify the expected outcome | Check return value or state changes |
Test Doubles
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Test Doubles ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā Dummy ā Objects passed but never used ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā Stub ā Provides canned answers to calls ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā Spy ā Records calls for verification ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā Mock ā Pre-programmed with expectations ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā Fake ā Working implementation (simplified) ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Test Structure
describe('Calculator', () => {
describe('add', () => {
it('should add two positive numbers', () => {
const calc = new Calculator();
const result = calc.add(2, 3);
expect(result).toBe(5);
});
});
});
Writing Testable Code
| Principle | Bad | Good |
|---|
| Pure Functions | Depends on global state | Takes all inputs as arguments |
| Dependency Injection | Creates dependencies internally | Receives dependencies |
| Single Responsibility | Does many things | Does one thing well |
| Avoid Side Effects | Modifies external state | Returns new values |
Code Coverage
| Metric | Description |
|---|
| Line Coverage | % of lines executed |
| Branch Coverage | % of branches (if/else) taken |
| Function Coverage | % of functions called |
| Statement Coverage | % of statements executed |
Summary
- ā¢Unit tests verify individual units in isolation
- ā¢AAA pattern structures tests clearly
- ā¢Test doubles replace real dependencies
- ā¢Good tests are fast, isolated, repeatable
- ā¢100% coverage doesn't mean bug-free