Now accepting guest editorial from SAP, Oracle, Workday and ServiceNow practitioners.Pitch a guide
The Sign-Off Table

Lesson 3 of 6

Isolating behavior: transactional buffer and CDS test doubles

11 min

Listen to this lesson

0:00 / 18:16

Scenario tests are honest but heavy: every one runs the whole save sequence. To prove one rule, such as "validateDeposit rejects a zero deposit for CMC-ALT-100", Kenji wants to call the validation alone, with exactly the instances it needs and nothing else. RAP supports that in two ways: calling a handler method directly from a local test class, and replacing the BO's transactional buffer with a double. A third tool, the mock EML API, serves code that consumes the BO.

Calling a handler method directly

Handler methods are private methods of local classes inside the behavior pool. SAP's ABAP Unit cheat sheet shows how a test reaches them: declare the test class deferred, make it a friend of the handler, and create the handler under test.

  1. In the behavior pool's Test Classes tab, declare CLASS ltc_item_validations DEFINITION DEFERRED FOR TESTING.
  2. In the Local Types tab, extend the handler definition: CLASS lhc_item DEFINITION INHERITING FROM cl_abap_behavior_handler FRIENDS ltc_item_validations.
  3. In the test class, CREATE OBJECT cut FOR TESTING. The cheat sheet uses this form for handler classes.
  4. Declare the response parameters with the right RAP types: validations and save-phase methods take RESPONSE FOR FAILED LATE and RESPONSE FOR REPORTED LATE; actions and interaction-phase methods take the EARLY variants.
  5. Call the method with the keys of the instances to check, then assert on the response parameters.
A handler unit test: the test fills the buffer double, calls the handler as a friend, and asserts the LATE responses the handler fills.
The Sign-Off Table · thesignofftable.com/academy/sap-abp-304

The transactional buffer double

A validation reads instances from the transactional buffer with READ ENTITIES ... IN LOCAL MODE. In a test, the buffer should contain exactly two items: one valid, one with a zero deposit. The transactional buffer double, class CL_BOTD_TXBUFDBL_BO_TEST_ENV, provides that. The cheat sheet's lifecycle is:

WhenCallPurpose
class_setupcl_botd_txbufdbl_bo_test_env=>create( cl_botd_txbufdbl_bo_test_env=>prepare_environment_config( )->set_bdef_dependencies( ... ) )One environment for the BDEF the handler reads
setupclear_doubles( ), then get_test_double( ... )Fresh buffer double before every test
Arrangetest_double->insert_test_data( instances = ... )Put the valid and invalid items into the buffer double
class_teardowndestroy( )Remove the redirection

Lifecycle of the transactional buffer double, following SAP's ABAP Unit cheat sheet.

With the double in place, ltc_item_validations->zero_deposit_rejected inserts item 10 for CMC-ALT-100 with DepositPerUnit 45.00 and item 20 with 0.00, calls cut->validatedeposit( EXPORTING keys = ... CHANGING failed = failed reported = reported ) and asserts that failed-item contains only item 20 and reported-item has one message for it. A second method passes only the valid item and asserts both responses are initial. Each test runs in milliseconds and touches no table.

The handler cannot tell the double from the real buffer, so the test controls exactly what it sees.
The Sign-Off Table · thesignofftable.com/academy/sap-abp-304

The mock EML API for consumers

Contoso's refund handler, ZCL_CMC_CORE_REFUND, reads a closed agreement with EML and creates a refund request. Here the BO is a dependency, not the code under test. The mock EML API, CL_BOTD_MOCKEMLAPI_BO_TEST_ENV, lets the test say: "when the code reads agreement 100045, return this result". The cheat sheet's pattern builds input and output with the builder factory cl_botd_mockemlapi_bldrfactory, configures the double with configure_call( )->for_read( )->when_input( ... )->then_set_output( ... ) and checks interactions with verify( )->read( ... )->is_called_times( times = 1 ).

QuestionUseContoso example
Does my handler judge these instances correctly?Transactional buffer doublevalidateDeposit with a zero-deposit item
Does my consumer react correctly to what the BO returns?Mock EML APIRefund handler reading a CLOSED agreement
Does my consumer call the BO the expected number of times?Mock EML API with verifyExactly one read per refund event
Does my view compute the right value?CDS test environmentOpen deposit sum in the analytics view
Do my handlers work together?No double for the BO: EML scenario testCreate, commit, read back
Which RAP double for which question.
The Sign-Off Table · thesignofftable.com/academy/sap-abp-304

CDS test doubles for view logic

Some rules live in CDS, not in ABAP. Contoso's analytical view ZI_CMC_OpenDeposits (illustrative) sums deposits of OPEN agreements per customer. The CDS test environment replaces that view's data sources with doubles, so the test inserts three agreements, two OPEN and one CLOSED, and asserts the sum covers only the OPEN ones. This tests the view's logic, not the BO; keep these tests in their own class.

One more practical point: the buffer double and the direct handler call are used together, not as alternatives. The direct call decides which method runs; the double decides what data it sees. A handler test that calls the method without a double reads the real buffer, which is empty in a unit test, so the handler finds nothing and the test passes for the wrong reason. Assert that the handler actually read the instances you inserted, for example by checking that the valid item produced no entry while the invalid one did.

What goes wrong in testing

  • Wrong response type. Declaring failed as EARLY for a validation gives a syntax error or a signature mismatch. Validations use LATE.
  • Over-mocking. A scenario test that mocks the BO it is supposed to test proves only the mock. Mock the BO only when it is a dependency.
  • Verifying too much. Asserting call counts on every read makes harmless refactoring fail. Verify interactions only when the interaction is the behavior.
  • Leaking environments. Missing destroy in class_teardown lets redirection leak into later test classes in the same run.

What would you do?

Elena's new class <code>ZCL_CMC_CORE_REFUND</code> must create exactly one refund per closed agreement. Her unit test currently creates a real agreement through EML and commits it. What should she change?

Key points
  • Handler methods are tested directly from a friend test class created with CREATE OBJECT ... FOR TESTING.
  • Validations take FAILED LATE and REPORTED LATE responses; actions take the EARLY variants.
  • CL_BOTD_TXBUFDBL_BO_TEST_ENV doubles the transactional buffer for the BO's own handlers.
  • CL_BOTD_MOCKEMLAPI_BO_TEST_ENV configures EML results for code that consumes the BO.

Check your understanding · not graded

Lesson check

  1. 1. How does a local test class reach a private handler method of lhc_item?

  2. 2. Which response types does a validation handler test declare?

  3. 3. Which class creates a double of the BO's transactional buffer?

  4. 4. When is the mock EML API the right double?

  5. 5. What happens if class_teardown does not call destroy?