Unit Testing RAP Behavior with ABAP Mock Framework
The built-in ABAP mock framework lets you test your behavior implementations without a real database commit. Full walkthrough with a sales order scenario.
Why Mock RAP?
Testing RAP behavior classes the naive way — just calling MODIFY ENTITIES and checking the DB — has serious drawbacks:
- Tests pollute the database and require cleanup
- They run slowly (network + DB round-trip)
- They can’t run in parallel without key collisions
The ABAP Mock Framework solves this by providing an in-memory implementation of the EML (Entity Manipulation Language) interface, so your handler methods run against a fake buffer, not the real DB.
Setup
CLASS ltc_sales_order DEFINITION FINAL
FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
PRIVATE SECTION.
DATA mo_cut TYPE REF TO ZBP_SALES_ORDER.
DATA mo_double TYPE REF TO if_abap_behv_test_environment.
CLASS-METHODS class_setup.
METHODS setup.
METHODS teardown.
METHODS test_create_sets_created_by FOR TESTING.
ENDCLASS.
Initializing the Environment
CLASS ltc_sales_order IMPLEMENTATION.
METHOD class_setup.
" One-time: register the CDS entity and its associations
mo_double = cl_abap_behv_test_environment=>create(
i_root_name = 'ZSALES_ORDER_HDR' ).
ENDMETHOD.
METHOD setup.
" Clear the fake buffer before each test
mo_double->clear_doubles( ).
ENDMETHOD.
METHOD teardown.
" Roll back any side effects (none with mock, but good practice)
ROLLBACK WORK.
ENDMETHOD.
Writing a Test
METHOD test_create_sets_created_by.
" Arrange — prepare input
DATA(ls_input) = VALUE zbp_sales_order_hdr=>tt_create(
( %cid = 'order_1'
SoldToParty = 'CUST-0042'
Currency = 'EUR' ) ).
" Act — call the handler directly
mo_cut = NEW #( ).
mo_cut->create(
entities = ls_input
mapped = DATA(ls_mapped)
failed = DATA(ls_failed)
reported = DATA(ls_reported) ).
" Assert
cl_abap_unit_assert=>assert_initial(
act = ls_failed-salesorder
msg = 'Create should not fail' ).
READ ENTITIES OF ZSALES_ORDER_HDR IN LOCAL MODE
ENTITY SalesOrder
FIELDS ( CreatedBy )
WITH VALUE #( ( %key = ls_mapped-salesorder[ 1 ]-%key ) )
RESULT DATA(lt_read)
FAILED DATA(ls_read_failed).
cl_abap_unit_assert=>assert_equals(
exp = sy-uname
act = lt_read[ 1 ]-CreatedBy
msg = 'CreatedBy must be set to current user' ).
ENDMETHOD.
ENDCLASS.
What the Mock Framework Does Under the Hood
When you call MODIFY ENTITIES ... IN LOCAL MODE inside augmentCreate,
the mock framework intercepts it and:
- Writes the entity data to an in-memory buffer (keyed by
%key) - Returns a synthetic
%cid_refmapping inmapped- - Lets subsequent
READ ENTITIES IN LOCAL MODEpull from that same buffer
No database access occurs. The test runs in milliseconds.
Common Pitfalls
- Forget
IN LOCAL MODE→ the real persistence layer is called, defeating the mock - Mixing
%cidand%keyincorrectly in the input →failedis populated but you don’t assert on it - Not calling
clear_doublesinsetup→ state leaks between test methods