On this page
The core idea
A characterisation test records what code currently does, not what it should do. For legacy code without tests, these tests create a safety net: after refactoring, the same inputs should produce the same outputs, including any odd behaviour you have decided to keep for now. AI assistants are useful here because they can read a function, suggest representative inputs and edge cases and draft tests quickly. You still decide which behaviours matter and confirm the expected values by running the code.
A practical approach
Step 1
Choose one function or module to refactor and identify its inputs, outputs and side effects.
Step 2
Ask the assistant to propose input cases, including edge cases such as empty values, boundaries and unusual formats.
Step 3
Generate the expected outputs by running the current code, not by asking the model to predict them.
Step 4
Review the tests for coverage of important paths and mark behaviour that looks like a bug for a separate decision.
Step 5
Refactor in small steps, running the characterisation tests after each, and replace them with behaviour-focused tests over time.
Worked example
An old fee-calculation function has nested conditions for currencies, discounts and taxes, and nobody wants to touch it. An assistant proposes 30 input combinations; running the current function produces the expected values, and the team saves them as parametrised tests. One case reveals that a tax is applied twice for a rare currency, which is logged as a separate bug. The refactor then proceeds with every other behaviour protected.
A mistake to avoid
Do not let the assistant invent expected values. A model can confidently predict an output that differs from what the code returns, and the test would then fail or, worse, encode a wrong expectation after you 'fix' the code to match.
Try it yourself
Pick an untested function in a project. Use an assistant to generate at least ten input cases, record actual outputs by running the code and commit them as tests before changing a single line.
Your implementation checklist
- The refactor target's inputs, outputs and side effects are listed
- Edge cases are included in the inputs
- Expected values come from running the current code
- Suspicious behaviour is recorded separately
- Refactoring happens in small, tested steps
Share or cite this guide
Innovate with Ajay Editorial (2026-10-07). Use AI to Write Characterisation Tests before Refactoring Legacy Code. https://innovatewithajay.com/blogs/use-ai-to-write-tests-for-legacy-code
