Characterization Testing
Characterization tests document the actual behavior of existing code. Unlike unit tests that verify intended behavior, characterization tests capture current behavior—bugs and all. They're essential for safely modifying legacy code.
📋 At a Glance
| Aspect | Details |
|---|---|
| Purpose | Document existing behavior, detect regressions |
| Approach | Test what code DOES, not what it SHOULD do |
| Coverage | Focus on code paths you'll modify |
| Output | Tests as executable documentation |
🎯 What You'll Learn
- Writing characterization tests for legacy code
- Golden Master testing for complex outputs
- Testing without seams (when dependency injection isn't available)
- Dealing with side effects in tests
- Test coverage strategy for legacy systems
Production Story: The Refactoring Disaster
A team refactored a pricing engine without tests:
JAVA(19 lines)CodeLoading syntax highlighter...
- Discount order changed (loyalty applied before category fee)
- Rounding happened at different point
- Edge case for
nullcustomer behaved differently
JAVA(11 lines)CodeLoading syntax highlighter...
🔬 Deep Dive
Pattern 1: Basic Characterization Test
JAVA(43 lines)CodeLoading syntax highlighter...
Pattern 2: Systematic Exploration
JAVA(41 lines)CodeLoading syntax highlighter...
Pattern 3: Golden Master Testing
JAVA(48 lines)CodeLoading syntax highlighter...
Pattern 4: Testing Without Seams
JAVA(81 lines)CodeLoading syntax highlighter...
Pattern 5: Capturing Side Effects
JAVA(51 lines)CodeLoading syntax highlighter...
Pattern 6: Database-Dependent Tests
JAVA(59 lines)CodeLoading syntax highlighter...
Pattern 7: Documenting Discovered Behavior
JAVA(69 lines)CodeLoading syntax highlighter...
⚠️ Common Mistakes
Mistake 1: Fixing Bugs in Characterization Tests
JAVA(14 lines)CodeLoading syntax highlighter...
Mistake 2: Over-Specifying Tests
JAVA(16 lines)CodeLoading syntax highlighter...
🐛 Debug This: The False Safety Net
A developer wrote characterization tests before refactoring. The tests passed before and after the refactoring. But production crashed with wrong calculations. "I had tests! How did this get through?"
JAVA(83 lines)CodeLoading syntax highlighter...
These aren't characterization tests - they're just regular mocked unit tests!
-
Mocks don't capture real behaviorJAVA(2 lines)CodeLoading syntax highlighter...
The mock doesn't return real tax rates. Characterization tests should use real data or captured production data.
-
Expected values are guessed, not discoveredJAVACodeLoading syntax highlighter...
The developer calculated 100 * 0.0725 = 7.25 mentally. But what if the real code does something different (like adding fees)?
-
Weak assertion on complex logicJAVACodeLoading syntax highlighter...
The exemption test only checks for non-null. Actual exemption logic isn't characterized at all.
-
Missing edge cases from production
- What happens with zero subtotal?
- What about negative amounts (returns)?
- Multiple exempt items?
- Tax rate of 0%?
JAVA(46 lines)CodeLoading syntax highlighter...
💻 Exercises
Exercise 1: Write Basic Characterization Test
⭐ Difficulty: Easy | ⏱️ Time: 10 minutes
JAVA(14 lines)CodeLoading syntax highlighter...
JAVA(66 lines)CodeLoading syntax highlighter...
Exercise 2: Golden Master Test
⭐⭐ Difficulty: Medium | ⏱️ Time: 20 minutes
JAVA(23 lines)CodeLoading syntax highlighter...
JAVA(102 lines)CodeLoading syntax highlighter...
Exercise 3: Capture Side Effects
⭐⭐ Difficulty: Medium | ⏱️ Time: 15 minutes
JAVA(22 lines)CodeLoading syntax highlighter...
JAVA(120 lines)CodeLoading syntax highlighter...
Exercise 4: Parameterized Characterization
⭐⭐⭐ Difficulty: Medium-Hard | ⏱️ Time: 20 minutes
JAVA(29 lines)CodeLoading syntax highlighter...
JAVA(133 lines)CodeLoading syntax highlighter...
Exercise 5: Database Characterization Test
⭐⭐⭐⭐ Difficulty: Hard | ⏱️ Time: 25 minutes
JAVA(32 lines)CodeLoading syntax highlighter...
JAVA(180 lines)CodeLoading syntax highlighter...
📝 Summary
| Technique | When to Use |
|---|---|
| Basic Characterization | Single method behavior |
| Parameterized Tests | Exploring input/output matrix |
| Golden Master | Complex outputs (reports, JSON) |
| Subclass and Override | Static dependencies |
| Side Effect Capture | Database writes, emails, etc. |
📅 Review Schedule for This Article
| Day | Task | Time |
|---|---|---|
| Day 1 | Review difference between characterization tests and unit tests | 5 min |
| Day 3 | Redo Exercise 1 (Basic Characterization Test) | 10 min |
| Day 7 | Practice golden master testing on a report in your codebase | 15 min |
| Day 14 | Redo Debug This (The False Safety Net) | 10 min |
| Day 30 | Write characterization tests for a legacy feature you'll modify | 25 min |