Lesson 3 of 5
Characterization tests
11 min
Listen to this lesson
Refactoring promises to change structure without changing behavior. That promise is only as good as your ability to detect a behavior change. Legacy ABAP rarely has unit tests, the specifications are long gone, and the people who wrote it have moved on. Characterization tests fill that gap: they record what the code does today, so you know when a change alters it.
What a characterization test is
Michael Feathers introduced the term in Working Effectively with Legacy Code. A characterization test, also called golden master testing, captures the actual behavior of existing code, not the desired behavior. You run the code with known inputs, record the outputs, and turn those outputs into assertions or a stored reference. The test passes as long as the behavior is unchanged. It does not tell you the code is correct; it tells you that you have not changed it.
- Pick inputsRepresentative and edge-case inputs: fleet and non-fleet customers, zero quantities, multi-currency, year boundaries
- IsolateControl dependencies: test doubles for tables, a fixed date, a fixed number provider
- Run and recordExecute the current code; capture outputs, messages and side effects
- Turn into assertionsWrite the recorded values into ABAP Unit tests or a reference file
- RefactorChange structure in small steps; run the tests after each one
- Investigate any differenceIntended change, or regression? Decide before going on
Characterization tests in ABAP
For Z_FLEET_REBATE_CALC, Kenji's team writes an ABAP Unit test class around the function module's current interface. Dependencies are isolated with the frameworks from the ABAP Unit course in this track: an SQL test double for the custom and SAP tables it reads, so the test controls the pricing data; a fixed date passed in or injected. Where the legacy code cannot be changed safely even to add an interface, a test seam or the function module test double framework can bridge the first step; SAP's Clean ABAP guide calls seams a temporary workaround, which is exactly their role here.
Then the team runs the function module for a matrix of inputs: 3 customers (Great Lakes Fleet Services, a second fleet customer, a non-fleet customer), 4 quantities (0, 1, 10, 500), 2 plants (1710 Detroit and 1720 Chicago, with standard costs of $150 and $156 for CMC-ALT-100) and 2 dates (mid-year and the last day of the year). Each result becomes an assertion. Forty-eight tests now describe today's behavior in seconds.
Process-level characterization
Some behavior lives in reports, postings and outputs rather than in a single method. For those, characterize at the process level: run the old and new versions on the same data in CMQ and compare the results. A rebate run produces rebate amounts per customer; compare them line by line. An invoice output produces documents; compare the relevant fields. This is the same reconciliation discipline used for data migration and performance work, applied to code changes.
| Level | Technique | Best for |
|---|---|---|
| Method or function module | ABAP Unit tests asserting recorded outputs, with test doubles | Calculation logic such as rebates and deposits |
| Program or job | Run old and new on the same input in CMQ; compare output files or tables | Reports, batch jobs, extracts |
| Business process | Execute a regression scenario before and after; compare postings and documents | Changes that touch several objects in a flow |
Characterize at the level where the behavior is observable.
Handling things that change on their own
- Dates and times: inject or pass them; never compare against the system clock.
- Generated numbers and timestamps: mask them in comparisons, or control them with a test double.
- Order of rows: sort before comparing; SAP HANA does not guarantee order without ORDER BY.
- Too much masking makes a characterization test meaningless. If you must mask most of the output, test at a different level.
- A characterization test fails after a change. Was the change meant to alter behavior?Yes → update the recorded value, with the reason in the commit and the owner's agreement · No → next question
- Is the difference caused by an unstable value such as a date or number?Yes → control or mask it, then rerun · No → next question
- Is the new behavior arguably more correct?Yes → still a behavior change: revert, then raise it with the owner as a separate fix · No → it is a regression: fix it before going on
What would you do?
While characterizing Z_FLEET_REBATE_CALC, Kenji's team finds that orders exactly on December 31 get no rebate, apparently due to a date comparison bug. The refactoring is half done. What should they do?
- Characterization tests record actual behavior so refactoring can be checked for unintended change.
- In ABAP: unit tests around current interfaces, isolated with test doubles; seams only as a temporary bridge.
- Characterize reports and processes by running old and new on the same data and comparing.
- Control dates, numbers and ordering; raise discovered bugs with the owner instead of fixing them silently.
Check your understanding · not graded
Lesson check
1. What does a characterization test record?
2. Who coined the term characterization test?
3. A characterization test reveals an apparent bug. What is the right move?
4. Why must dates be controlled in characterization tests?