ABAP quality

ABAP Unit Test: Making Legacy Code Testable

How to write ABAP Unit tests for existing SAP code: seams, test doubles, database dependencies, and what to test first in legacy programs.

Updated September 2026Evidence-led guideABAP unit test
Built for

ABAP developers and development leads introducing automated unit tests into custom code that was written without them.

Decision supported

Where to start with ABAP Unit in a codebase that has none, and which dependencies have to be broken before a test is possible at all.

Decision context

ABAP Unit is part of the platform and costs nothing to adopt technically. What it costs is design, because most legacy ABAP is untestable for a specific reason: the logic and the database access are in the same routine, so there is no way to exercise the logic without the data.

So the work is not writing tests, it is creating seams. A seam is a place where you can substitute a test double for a real dependency without changing behavior. In ABAP that usually means moving database access behind an interface, or extracting the calculation from the routine that fetched the data.

This page is about the order to do that in, because attempting full coverage of a legacy program in one pass is how ABAP Unit initiatives die. Adranum's contribution is the impact side: knowing which code the next change actually reaches, so the seams get built where they will be used.

What to test first

Coverage is not the goal at the start. Confidence in the parts that change most often is. Pick in this order.

  • Pure calculations: pricing, tax, discount, allocation, rounding, date arithmetic. They usually need no seam at all and they are where defects are most expensive.
  • Validation and eligibility rules, which are branch-heavy and where a table of cases pays for itself immediately.
  • Conversion and mapping logic, especially anything that formats data for an interface, because the failure mode is silent.
  • Code you are about to change. A test written just before a change is the cheapest test you will ever write and the one most likely to catch something.
  • Code that has broken before. Recurrence is the strongest predictor available.

Creating seams in legacy ABAP

These are the standard moves, in roughly increasing order of effort. Each one makes a class of tests possible that was not possible before.

  • Extract the calculation. Move the logic that operates on data into a method that receives that data as parameters and returns a result. This alone makes most business logic testable.
  • Put database access behind an interface. Define an interface for the reads a class needs, implement it once against the database and once as a test double, and inject it through the constructor.
  • Inject dependencies through the constructor rather than instantiating them inside the method, so a test can supply a substitute.
  • Replace static calls to global state with an injected collaborator, because a static call cannot be substituted.
  • Separate the selection screen and the output from the logic. A report whose logic can only run through its own selection screen is testable only through the screen.
  • Where a legacy call genuinely cannot be moved, ABAP Unit's test double and injection facilities can substitute some dependencies, but treat that as a bridge rather than a design.

Rules that keep the suite trustworthy

A unit test suite that is slow, flaky, or dependent on the state of the system will be switched off within a quarter. These rules are what prevent that.

  • A unit test must not depend on database content, the current date, the current user, or configuration. If it does, it is an integration test and belongs in a different category.
  • A unit test must be repeatable. Running it twice in a row must produce the same result without cleanup.
  • Test method names should state the expectation, so a failure report reads as a sentence about the business rule rather than a method number.
  • One assertion subject per test. A test that checks six things reports the first failure and hides the rest.
  • Tests belong in the same transport as the code they test, so they cannot drift out of sync.
  • Make the test class part of the definition of done for a change, and enforce it in the code review rather than in a policy document.

What the workflow must cover

  • Change-first targeting. Identify the code the next release actually reaches so seams and tests are built where they will be used rather than where coverage is easiest.
  • Dependency visibility. Show what a routine calls and what calls it, which is the information the extraction depends on.
  • Coverage as a gap list. Report impacted code with no test rather than a coverage percentage, because the percentage does not say which risk is uncovered.
  • Result binding. Attach test outcomes to the package under review so an approval does not survive a later change.

Implementation workflow

Start with a bounded customer scenario and explicit acceptance criteria. Preserve native SAP permissions and accountable review while the software creates a repeatable evidence chain.

  1. Pick the calculation or rule that changes most often.
  2. Extract it into a method that takes data and returns a result.
  3. Write the test cases as a table of inputs and expected outputs.
  4. Introduce an interface and injection where database access blocks a test.
  5. Make a test class part of the definition of done for the next change to that code.

Evidence to require

A transformation claim should resolve to observable artifacts, decisions, and execution receipts. Ask for the following evidence in a representative evaluation:

  • Test class per unit under test
  • Case table of inputs and expectations
  • Injected interface and double
  • Execution result per package
  • Impacted code with no test
  • Recurrence history
  • Review record enforcing the test
  • Runtime of the suite

Boundaries and non-claims

Adranum separates analysis, proposal, human review, package creation, customer-local validation, and production execution. A later state never rewrites the evidence that supported an earlier decision.

  • Unit tests do not prove integration. Cross-system behavior needs the integration cycle regardless of unit coverage.
  • Some legacy routines cannot be made testable without a rewrite, and saying so is a better answer than a test that asserts nothing.
  • Coverage percentage is a weak signal. Which impacted paths are untested is the useful measure.

Buyer checklist

  • Which code changes most often, and is it tested?
  • Where does database access block a test?
  • Does any test depend on system state, date, or user?
  • Are tests transported with the code they test?
  • Which impacted code has no test at all?

Practical answers

Can ABAP Unit test a report that reads the database directly?

Not as a unit test. Extract the logic into a method that receives the data as parameters, or put the reads behind an injectable interface. Until there is a seam, the only thing you can test is the whole report against real data.

What is a realistic ABAP Unit coverage target for legacy code?

Coverage of the whole codebase is the wrong target. A better one is that every change from now on ships with a test for the logic it touches, which builds coverage where change actually happens.

Should ABAP Unit tests be transported to production?

Yes, in the same transport as the code. Splitting them means the code and its tests diverge, and a test that does not exist in the system where the code runs cannot be re-run against it.

Continue the evaluation

Related SAP workflows