Boss: test a legacy service
InvoiceTotal was ported line for line from the old billing system in 2019. Finance reconciles against its totals every month. It has one test, which checks the total is more than £10. Next sprint, someone wants to refactor it.
Before then, it needs characterisation tests: tests that record what the code does today, so a refactor that changes any total goes red.
Record what it does, not what it should do
A characterisation test isn't a spec. You don't decide the right answer and assert it. You run the code, write down what it gives, and assert that. If it does something odd, the test pins the odd thing and its name says so:
yield 'quirk: an empty invoice abroad still pays the surcharge' => [[], 'DE', null, 250];
Then raise the quirk with whoever owns the behaviour. Fixing it might be right, but it's a change to the totals finance sees, and it's a separate decision, made on purpose, with its own commit. While you're pinning, the code is right by definition.
Your checklist
| Level | Ask of your tests |
|---|---|
| 4.1 What's worth testing | Does every test check a total? Nothing that only proves a type. |
| 4.2 Data providers | Is every decision in of() covered on both sides, as named cases? |
| 4.3 Fakes over mocks | Is TaxRates a small fake with different rates per country, not a stub returning one number? |
| 4.4 Time, randomness and I/O | Nothing here reads the clock. Check that before you trust a legacy test. |
| 4.5 Mutation testing | Would each planted bug fail an exact assertSame()? |
Start by reading of() and listing every if. Each one is at least two cases.