Appearance
Lesson 22 — ABAP Unit, Debugging, ATC & Performance
Guided learning: 3 hours
Independent practice: 3 hours (separate file)
Environment: SAP BTP ABAP Environment Trial + Eclipse with ABAP Development Tools (ADT)
Prerequisites: Basic ABAP classes, methods, internal tables, and ABAP SQL. No objects from previous lessons are required.
1. The business problem: a calculation is wrong
Imagine you maintain an order-processing application. A user reports:
“Sometimes the total is too low. Also, customers buying exactly ten items are not receiving their discount.”
The application activates successfully. It does not produce a runtime error. However, the calculation gives the wrong business result.
In a real development team, you should not just change code until the result looks right. You need to reproduce the problem, identify its cause, correct it, and show that the fix has not broken other cases.
This lesson teaches four everyday development skills:
- ABAP Unit: automatically check that methods return the right result.
- Debugger: stop a program, inspect variable values, and discover why a test fails.
- ABAP Test Cockpit (ATC): check code for potential quality and ABAP Cloud issues.
- Performance: ask the database for only the rows and columns you need.
Our workflow:
text
Bug report → Reproduce → Write failing test → Debug
→ Fix → Tests pass → ATC check → SQL reviewImportant: The first code example contains intentional logical bugs. It is supposed to activate and then fail two tests. Do not fix those lines until you have seen the failures.
2. Create the guided exercise package and class
Step 2.1 — Open the ABAP project
- Start Eclipse.
- Open the ABAP perspective using Window → Perspective → Open Perspective → Other… → ABAP if necessary.
- In Project Explorer, expand your SAP BTP ABAP Trial project.
- Choose an existing writable package, or create a new package named
ZPKG_L22_001(substitute your assigned suffix for001).
Step 2.2 — Create a class
- Right-click the package.
- Choose New → ABAP Class. If it is not listed, use New → Other ABAP Repository Object…, and search for ABAP Class.
- Enter the name
ZCL_L22_ORDER_TOTAL_001. - Description:
Lesson 22 - Order calculation and tests. - Complete the wizard, selecting a transport request if requested.
- Replace the generated global class source with the code below.
Use a unique suffix consistently in the class name, every reference to the class, and the later SQL table name. The names shown here use
001only as a classroom example.
Step 2.3 — Paste the intentionally faulty class
The business rules are:
- Merchandise subtotal = unit price × quantity.
- Orders of 10 or more items receive 10% off merchandise only.
- The delivery fee is added after the discount.
- For this exercise, use nonnegative prices and delivery fees and positive quantities.
abap
CLASS zcl_l22_order_total_001 DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
CLASS-METHODS calculate_total
IMPORTING
iv_unit_price TYPE decfloat34
iv_quantity TYPE i
iv_delivery_fee TYPE decfloat34
RETURNING VALUE(rv_total) TYPE decfloat34.
ENDCLASS.
CLASS zcl_l22_order_total_001 IMPLEMENTATION.
METHOD calculate_total.
rv_total = iv_unit_price * iv_quantity.
" Deliberate bug 1: the exact boundary is handled incorrectly.
IF iv_quantity > 10.
rv_total = rv_total * CONV decfloat34( '0.9' ).
ENDIF.
" Deliberate bug 2: delivery is subtracted, not added.
rv_total = rv_total - iv_delivery_fee.
ENDMETHOD.
METHOD if_oo_adt_classrun~main.
DATA(total) = calculate_total(
iv_unit_price = CONV decfloat34( '40' )
iv_quantity = 2
iv_delivery_fee = CONV decfloat34( '5' ) ).
out->write( |Calculated total: { total }| ).
out->write( 'Expected business total: 85' ).
ENDMETHOD.
ENDCLASS.Step 2.4 — Activate and run
- Save using Ctrl+S.
- Activate using Ctrl+F3.
- Right-click the class editor and choose Run As → ABAP Application (Console), or use F9.
- Read the ABAP Console output.
Expected result before the fix:
text
Calculated total: 75
Expected business total: 85The code activates, but the calculation is incorrect. Activation verifies syntax, not business correctness.
3. Understand the expected results before writing tests
Work out these examples yourself. A 10% discount means multiplying the merchandise subtotal by 0.90, then adding the delivery fee.
| Scenario | Quantity | Unit price | Delivery | Expected total |
|---|---|---|---|---|
| Below discount threshold | 1 | 20 | 0 | 20 |
| Delivery fee | 2 | 40 | 5 | 85 |
| Exactly 10 items | 10 | 30 | 5 | 275 |
| More than 10 items | 12 | 20 | 0 | 216 |
An ABAP Unit test can check each example automatically. It will report a failure when the method produces something different.
4. Create ABAP Unit tests in Eclipse ADT
Step 4.1 — Open the Test Classes tab
- Open
ZCL_L22_ORDER_TOTAL_001in the ABAP editor. - At the bottom of the editor, select Test Classes (near the Global Class / Local Types tabs; the exact tab layout can differ by ADT version).
- The test-class include will be created automatically if it does not exist.
- Paste the code below into Test Classes, not into the Global Class source.
This separation matters: the main class contains production logic, while the test classes are executed by the ABAP Unit framework.
Step 4.2 — Paste these four tests
abap
CLASS ltc_order_total DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS below_threshold FOR TESTING.
METHODS delivery_fee FOR TESTING.
METHODS exactly_ten FOR TESTING.
METHODS above_threshold FOR TESTING.
ENDCLASS.
CLASS ltc_order_total IMPLEMENTATION.
METHOD below_threshold.
DATA(actual) = zcl_l22_order_total_001=>calculate_total(
iv_unit_price = CONV decfloat34( '20' )
iv_quantity = 1
iv_delivery_fee = CONV decfloat34( '0' ) ).
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '20' )
act = actual
msg = 'A single item must not receive a discount' ).
ENDMETHOD.
METHOD delivery_fee.
DATA(actual) = zcl_l22_order_total_001=>calculate_total(
iv_unit_price = CONV decfloat34( '40' )
iv_quantity = 2
iv_delivery_fee = CONV decfloat34( '5' ) ).
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '85' )
act = actual
msg = 'Delivery must be added to the merchandise total' ).
ENDMETHOD.
METHOD exactly_ten.
DATA(actual) = zcl_l22_order_total_001=>calculate_total(
iv_unit_price = CONV decfloat34( '30' )
iv_quantity = 10
iv_delivery_fee = CONV decfloat34( '5' ) ).
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '275' )
act = actual
msg = 'The discount begins at exactly ten items' ).
ENDMETHOD.
METHOD above_threshold.
DATA(actual) = zcl_l22_order_total_001=>calculate_total(
iv_unit_price = CONV decfloat34( '20' )
iv_quantity = 12
iv_delivery_fee = CONV decfloat34( '0' ) ).
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '216' )
act = actual
msg = 'A quantity above ten receives a ten-percent discount' ).
ENDMETHOD.
ENDCLASS.Step 4.3 — Run the tests
- Save and activate the class and its test-class include.
- Right-click the class in Project Explorer or the editor.
- Choose Run As → ABAP Unit Test, or press Ctrl+Shift+F10.
- Open the ABAP Unit view if it is not displayed automatically: Window → Show View → Other…, then search for ABAP Unit.
- Expand the results to see individual test methods.
Expected before correction: four test methods are discovered; below_threshold and above_threshold pass; delivery_fee and exactly_ten fail. The test names and messages help identify the business rules being violated.
If no tests are discovered, check that your code is in the Test Classes tab, that each test method has FOR TESTING, and that the objects are activated.
5. Debug the incorrect result
A test tells you what is wrong. The debugger helps you understand why.
Step 5.1 — Add a breakpoint
- Open the Global Class tab for
ZCL_L22_ORDER_TOTAL_001. - Find the line
IF iv_quantity > 10.. - Double-click the left margin beside the line number to set a breakpoint.
- Add another breakpoint to
rv_total = rv_total - iv_delivery_fee.. - Execute the class using F9, or run the unit test that calls this method.
- If Eclipse prompts you to switch to the Debug perspective, accept.
Step 5.2 — Inspect what happens
In the debugger:
- Open Variables and inspect
iv_quantity,iv_unit_price,iv_delivery_fee, andrv_total. - Use Step Over (F6) to execute a statement without stepping into method calls.
- Use Resume (F8) to continue to the next breakpoint or program completion.
- Observe that with quantity 2, the subtotal is 80, then the delivery fee changes it to 75 instead of 85.
- To reproduce the exact-threshold defect, debug the
exactly_tentest or change the console call toiv_quantity = 10,iv_unit_price = 30andiv_delivery_fee = 5.
If you change the console inputs, also change its expected-business-total message to 275: the correct calculation is (10 × 30 × 0.90) + 5. After inspecting this case, restore the console inputs to quantity 2, unit price 40, and delivery fee 5, and restore the expected-total message to 85 before continuing to Section 6.
If the debugger does not stop, check that the class is activated, the breakpoint is enabled, and you are executing in the same ADT project/user context. For this exercise the console and ABAP Unit tests are both valid entry points.
Step 5.3 — Write a short diagnosis
In your notes, record:
- Which line caused the delivery-fee problem?
- Which condition excluded an order containing exactly ten items?
- Why did both lines pass the syntax check?
6. Fix the class and prove the correction
Replace only the calculate_total method with the following corrected implementation:
abap
METHOD calculate_total.
rv_total = iv_unit_price * iv_quantity.
IF iv_quantity >= 10.
rv_total = rv_total * CONV decfloat34( '0.9' ).
ENDIF.
rv_total = rv_total + iv_delivery_fee.
ENDMETHOD.Save and activate. Then:
- Run Ctrl+Shift+F10 again.
- Verify that all four ABAP Unit tests pass.
- Run the class in the console again with the original inputs (quantity
2, unit price40, delivery fee5); its total should now be85. - Remove or disable the breakpoints when you are finished.
Important habit: Keep the tests. They will protect this calculation from accidental regressions when somebody changes it later.
7. Run the ABAP Test Cockpit (ATC)
ABAP Unit verifies the behavior you explicitly test. ATC checks code quality and compliance rules, such as selected syntax, maintainability, security, or performance patterns, depending on the configured check variant. ATC does not know whether an order total should include delivery unless you write a test for it.
Step 7.1 — Run a check
- Open the corrected
ZCL_L22_ORDER_TOTAL_001class in Eclipse ADT. - Right-click in the editor or on the class in Project Explorer.
- Choose Run As → ABAP Test Cockpit, or use Ctrl+Shift+F2.
- Wait for the check to finish.
- Inspect the ATC Problems view; open it through Window → Show View → Other… if needed.
Step 7.2 — Interpret the result
- No findings: This is a valid result for a small, clean class. Do not invent an ATC warning.
- A finding appears: Read its check name, location, explanation, and severity. Correct a genuine problem, then rerun ATC.
- ATC is unavailable or permission is denied: Record the restriction and continue with ABAP Unit, debugging, and SQL review. Do not assume that every Trial tenant has identical ATC permissions or check variants.
Try Run As → ABAP Test Cockpit With… only if your environment offers a suitable alternate check variant; do not invent check-variant names.
8. Improve data access: ask the database for less data
A different performance problem occurs when code reads more rows and columns than the task needs. This can waste work in the database, network, and ABAP application server. SAP recommends selecting only the required rows and fields.
For this small lab, you will create a table that belongs to you. You will not access or change SAP-delivered tables or the shared S/4HANA system.
Step 8.1 — Create a database table
- Right-click your package in Eclipse ADT.
- Choose New → Other ABAP Repository Object….
- Search for Database Table and choose it.
- Name the table
ZL22_ORDERS_001and enter a description. - Replace the table definition with this code:
abap
@EndUserText.label : 'Lesson 22 SQL Performance Orders'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zl22_orders_001 {
key client : abap.clnt not null;
key order_id : abap.numc(6) not null;
customer : abap.char(20);
priority : abap.char(1);
amount : abap.dec(13,2);
}- Save and activate using Ctrl+F3.
Step 8.2 — Create a console class to seed and read the table
Create another ABAP class named ZCL_L22_SQL_LAB_001 in the same package and paste the following complete code:
abap
CLASS zcl_l22_sql_lab_001 DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_l22_sql_lab_001 IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
" These are demonstration records in our own table.
TYPES ty_rows TYPE STANDARD TABLE OF zl22_orders_001
WITH EMPTY KEY.
DATA(rows) = VALUE ty_rows(
( order_id = '000001'
customer = 'ALICE' priority = 'H' amount = '100' )
( order_id = '000002'
customer = 'BOB' priority = 'N' amount = '50' )
( order_id = '000003'
customer = 'CARLA' priority = 'H' amount = '200' )
( order_id = '000004'
customer = 'DAVID' priority = 'N' amount = '70' )
( order_id = '000005'
customer = 'ELENA' priority = 'H' amount = '125' ) ).
MODIFY zl22_orders_001 FROM TABLE @rows.
" Example A: reads all columns and all rows in this table.
SELECT FROM zl22_orders_001
FIELDS *
INTO TABLE @DATA(all_orders).
" Example B: reads only the two columns and rows we need.
SELECT FROM zl22_orders_001
FIELDS order_id, amount
WHERE priority = 'H'
INTO TABLE @DATA(high_priority_orders).
out->write( |Rows read by broad query: { lines( all_orders ) }| ).
out->write( |High-priority rows: { lines( high_priority_orders ) }| ).
out->write( high_priority_orders ).
ENDMETHOD.
ENDCLASS.Activate and execute with F9.
For a new, student-specific table, the expected counts are 5 rows in the broad result and 3 high-priority rows. If you use a shared table name containing additional records, counts may differ; use a unique suffix.
Step 8.3 — Compare the two SELECT statements
| Query A | Query B |
|---|---|
FIELDS * | FIELDS order_id, amount |
No WHERE | WHERE priority = 'H' |
| Reads all demo rows and columns | Reads only required demo rows and columns |
| Useful to inspect a tiny demo | Better pattern for a selective business requirement |
Do not interpret this five-row demonstration as a benchmark. The lesson shows how to reduce unnecessary data transfer; it does not prove a measurable runtime improvement. Actual performance work requires representative data volumes and profiling or SQL trace tools where available.
Step 8.4 — Think about an API equivalent
In Lesson 21, the OData client proxy supported filters and set_top( ). That is the same broad principle: restrict data as close to its source as possible rather than fetch everything and discard most of it in ABAP.
9. What you should now be able to do
Before moving to independent practice, check that you can:
10. Prepare for independent practice
You will receive a different maintenance ticket: repair a service-ticket response-time calculation. You will start from a working-but-incorrect class, design your own tests, debug it, run ATC, and explain one performance improvement.
Do not start by changing the class immediately. First write down the expected business behavior and observe the failing tests.
Official references
- SAP Help — How to Write ABAP Unit Test Classes
- SAP Help — Run Modes of ABAP Unit Tests
- SAP Help — Debugging ABAP Code
- SAP Help — Launching ATC Check Run from Project Explorer
- SAP Help — ABAP SQL Performance Notes
- SAP Learning — Implementing Basic SELECT Statements
Environment note: The ABAP examples follow the SAP BTP ABAP Cloud / ADT programming model, but they have not been activated in your individual Trial tenant. If a generated wizard screen or available check variant differs, use the options exposed by your installed ADT and document the difference.