Behavior Implementations
RAP / CDS / ODataadvanced

Behavior Implementations Part 5: Performance, Unit Testing and Debugging

Key takeaway

Keep behavior implementations fast, tested and diagnosable: set-based handler design, EML-based ABAP Unit tests with isolation, and the tools to debug RAP requests coming from Fiori or APIs.

Explanation

📌 KEY POINTS
✅ Handlers are set-based: one READ, one MODIFY.

✅ Test with EML + COMMIT ENTITIES; isolate with test doubles.

✅ Debug Fiori calls with external breakpoints and ABAP Cross Trace.

⚠️ Always include a bulk test (hundreds of instances).

▶ WHERE WE ARE
Parts 1–4 covered how to write behavior code. This part covers how to make it fast, prove it works, and find problems quickly when it does not.

━━━━━━━━━━━━━━━━━━━━
▶ 1. PERFORMANCE — DESIGN FOR SETS

Handlers receive tables of keys. One mass action or API batch can send hundreds. Rules:

  • One READ ENTITIES for all keys, with only the fields needed.
  • One MODIFY ENTITIES with a table, never one per key.
  • External data (master data, customizing) read once for all keys — join or FOR ALL ENTRIES after an emptiness check — never SELECT SINGLE in a loop.
  • Keep determination triggers narrow; a determination that changes fields which trigger other determinations can cascade.
  • Avoid reading children when only root fields are needed.
  • Measure with production-like volumes: SQL trace (ST05) for database statements, ABAP profiler (SAT or ADT profiling) for ABAP runtime.

━━━━━━━━━━━━━━━━━━━━
▶ 2. UNIT TESTING WITH EML

Test the behavior the way consumers use it: through EML.

  • Test class in the behavior pool (Test Classes tab) or a separate test class, "FOR TESTING".
  • Arrange with %cid; Act with MODIFY ENTITIES (and COMMIT ENTITIES RESPONSE when the save sequence matters); Assert on failed, reported and read-back data; ROLLBACK ENTITIES in teardown.
  • Test each rule: every validation (pass and fail), every determination (including idempotency: run twice), every action (valid and invalid state), feature control results (READ ENTITIES … RESULT with GET PERMISSIONS or by attempting the operation), and bulk behavior (e.g. 200 keys).
  • Isolation: tests that commit write to the database. Use test doubles: the ABAP SQL test double framework (cl_osql_test_environment) for tables and CDS views, and — on releases that provide them — RAP BO test doubles for other BOs your code calls.
  • Unit-test pure business logic (calculations) in separate classes without EML — fastest and simplest.

━━━━━━━━━━━━━━━━━━━━
▶ 3. DEBUGGING RAP REQUESTS

  • Breakpoints in handler methods work for EML tests and console classes directly.
  • Requests from Fiori or APIs run under another user/session: set external (user) breakpoints in ADT for the calling user, or for all requests of your user when testing via preview.
  • ABAP Cross Trace (in ADT) records RAP processing of a request — which operations, determinations and validations ran, with which instances and results — without setting breakpoints. Very useful to see why a determination did not trigger.
  • Gateway-level errors: /IWFND/ERROR_LOG and /IWBEP/ERROR_LOG; dumps: ST22.
  • Typical questions when debugging: Did the trigger fields actually change? Was the instance in draft (%is_draft)? Did feature control or authorization reject before your code ran? Is the message in reported but missing %tky?

━━━━━━━━━━━━━━━━━━━━
▶ 4. CODE REVIEW CHECKLIST

  • All methods process all keys; no keys[ 1 ].
  • %tky everywhere; IN LOCAL MODE inside the BO.
  • No COMMIT/ROLLBACK, no direct DML on BO tables, no external calls in determinations/validations.
  • No SELECT in loops; FOR ALL ENTRIES guarded.
  • Every failed has a reported message; validations use %state_area.
  • Tests exist for each rule; bulk test exists.

━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP

  1. Add a test class with tests for one validation, one action and one bulk case (code example).
  2. Run with Ctrl+Shift+F10 in ADT; add to ATC/CI.
  3. Record an ABAP Cross Trace for a Fiori save and read which methods ran.

━━━━━━━━━━━━━━━━━━━━
▶ WHAT YOU HAVE AFTER THE SERIES

Implementations that are correct, fast and testable. Next topic: EML in depth.

Code example

ABAP Code
" ===== Test class (Test Classes tab of ZBP_R_PURCHREQ) =====CLASS ltc_purchreq DEFINITION FINAL FOR TESTING  DURATION SHORT RISK LEVEL HARMLESS.  PRIVATE SECTION.    METHODS description_is_required FOR TESTING.    METHODS initial_status_is_new    FOR TESTING.    METHODS approve_many_requests    FOR TESTING.    METHODS teardown.ENDCLASS. CLASS ltc_purchreq IMPLEMENTATION.   METHOD description_is_required.    MODIFY ENTITIES OF zr_purchreq      ENTITY PurchaseRequest        CREATE FIELDS ( CurrencyCode ) WITH VALUE #( ( %cid = 'T1' CurrencyCode = 'EUR' ) ).     COMMIT ENTITIES RESPONSE OF zr_purchreq      FAILED DATA(failed) REPORTED DATA(reported).   " validation runs here - nothing saved on failure     cl_abap_unit_assert=>assert_not_initial( act = failed-purchaserequest ).    cl_abap_unit_assert=>assert_not_initial( act = reported-purchaserequest ).  ENDMETHOD.   METHOD initial_status_is_new.    MODIFY ENTITIES OF zr_purchreq      ENTITY PurchaseRequest        CREATE FIELDS ( Description CurrencyCode )        WITH VALUE #( ( %cid = 'T2' Description = 'Test' CurrencyCode = 'EUR' ) )      MAPPED DATA(mapped).     READ ENTITIES OF zr_purchreq      ENTITY PurchaseRequest FIELDS ( Status )      WITH VALUE #( ( %tky = mapped-purchaserequest[ 1 ]-%tky ) )      RESULT DATA(result).     cl_abap_unit_assert=>assert_equals( exp = 'N' act = result[ 1 ]-Status ).  ENDMETHOD.   METHOD approve_many_requests.    " bulk: 200 requests created and submitted in one call must not fail or time out    DATA creates TYPE TABLE FOR CREATE zr_purchreq.    creates = VALUE #( FOR i = 1 UNTIL i > 200                       ( %cid = |C{ i }| Description = |Bulk { i }| CurrencyCode = 'EUR' ) ).    MODIFY ENTITIES OF zr_purchreq      ENTITY PurchaseRequest CREATE FIELDS ( Description CurrencyCode ) WITH creates      FAILED DATA(failed).    cl_abap_unit_assert=>assert_initial( act = failed ).  ENDMETHOD.   METHOD teardown.    ROLLBACK ENTITIES.          " discard the transactional buffer  ENDMETHOD. ENDCLASS. " ===== Isolation for tests that must COMMIT successfully =====" DATA(sql_env) = cl_osql_test_environment=>create("                   i_dependency_list = VALUE #( ( 'ZPR_HEAD' ) ( 'ZPR_ITEM' ) ) )." ... run test ... sql_env->destroy( ). " ===== Debugging checklist =====" Fiori/API call   -> external (user) breakpoint in ADT for the calling user" What ran?        -> ABAP Cross Trace (RAP component) in ADT" Gateway errors   -> /IWFND/ERROR_LOG, /IWBEP/ERROR_LOG" Short dumps      -> ST22" Slow?            -> ST05 SQL trace, ADT profiler / SAT

Real project scenario

A mass-approval feature passed all tests with five orders and timed out in production with 800. An ABAP Cross Trace and an SQL trace showed that the approve action called a helper that selected the approver's cost-center data with SELECT SINGLE per key, and that the item total determination had a trigger on all fields, causing repeated re-reads.
The team rewrote the helper to read cost-center data once for all keys, narrowed the determination triggers, and added a bulk unit test with 200 instances to the CI run so the problem could not come back. The same test class also caught a later regression where a new validation processed only the first key.

Common mistakes

  • Testing only through the Fiori preview, by hand, with one instance.
  • Tests that commit real data without isolation.
  • No bulk test, so per-key database access goes unnoticed.
  • Debugging Fiori requests with normal breakpoints (they do not stop for other sessions).
  • Guessing why a determination did not run instead of checking triggers and draft state.
  • Broad determination triggers causing cascades.

Best practices

  • Set-based handlers: one READ, one MODIFY, external data read once.
  • EML-based ABAP Unit tests for every rule, plus a bulk test.
  • Isolate committing tests with test doubles; ROLLBACK ENTITIES in teardown.
  • Use external breakpoints and ABAP Cross Trace for UI and API requests.
  • Run tests and ATC checks in CI; use the review checklist in every pull request.

Interview angle

Questions: "How do you test a RAP BO?", "How do you debug a RAP request coming from Fiori?", "What are typical RAP performance problems?"
Strong answer: EML-based ABAP Unit tests with MODIFY, COMMIT ENTITIES RESPONSE and ROLLBACK ENTITIES, test doubles for isolation, and a bulk test; external breakpoints and ABAP Cross Trace for UI/API requests, error logs and ST22 for failures; performance problems come from per-key reads/modifies and broad determination triggers, measured with ST05 and the profiler.

Practice this lesson

Quick check

1. Mass approval works for five orders but times out for 800. Which tool shows the repeated database statements fastest?

2. When enabling draft handling for a RAP business object, which additional artifact must typically be created alongside the transactional behavior definition?

3. A RAP business object needs optimistic concurrency control so that OData clients must supply an ETag when updating an entity. How is this typically enabled in the behavior definition?

Interview questions

1. How do you test and debug a RAP behavior implementation effectively?

Answer it in your own words first, then compare.

2. You review an inherited behavior pool before go-live. What checklist do you apply?

Answer it in your own words first, then compare.

Practice more with Interview AI →

Reference-verified: the steps are checked against official SAP documentation and tutorials, and object and field names in the sample code are checked for consistency. The code has not been compiled or run on an SAP system by ERPClimb. Syntax marked release-dependent varies by SAP release, so check it in your own system.

More lessons in this topic

Interview questions on this topic

Related SAP topics