PHP 7: Maintaining and Upgrading Legacy Apps · Getting Legacy Code Under Test
Characterization Tests
Capture what the code does today before you change it.
Characterization tests record the current behavior of code — even if it seems wrong — so you notice when a refactor changes it. Call the code with representative inputs, capture outputs (return values, HTML, database rows), and assert on them. When a characterization test fails after a change, decide consciously whether the change was intended.
<?php
use PHPUnit\Framework\TestCase;
final class PriceCalculatorCharacterizationTest extends TestCase
{
/** @dataProvider cases */
public function testCurrentBehavior(array $cart, string $coupon, int $expected): void
{
// Values recorded by running the legacy code, not from a spec.
$this->assertSame($expected, calculate_price($cart, $coupon));
}
public function cases(): array
{
return [
'no coupon' => [[['price' => 1000, 'qty' => 2]], '', 2000],
'ten percent' => [[['price' => 1000, 'qty' => 2]], 'TEN', 1800],
'unknown coupon keeps price' => [[['price' => 500, 'qty' => 1]], 'BOGUS', 500],
];
}
}
Common pitfalls
- 'Fixing' surprising behavior inside a refactoring commit.
- Too few inputs to cover important branches.
- Characterization tests that depend on today's date or random data.
Key points
- Record current behavior, not intended behavior
- Cover important branches with varied inputs
- Separate refactoring from behavior changes
Further reading: Michael Feathers: characterization testing
Practice questions
How is a characterization test different from a normal unit test?
Answer
It documents what the code currently does (discovered by running it) rather than what a specification says it should do. Its goal is to detect any behavior change during refactoring.
What should you do if you find a bug while writing characterization tests?
Answer
Record it (test documents current behavior, with a note), finish the refactor safely, then fix the bug in a separate change with a test asserting the correct behavior.
Open in the app to mark lessons complete, take notes and track your progress.