Complete Mini Project, ABAP Unit Tests & Interview Revision
Build a production-style sales analytics service using modern ABAP, verify it with automated tests, and revise the most important interview concepts.
Project scenario
A business user needs a report that summarizes sales orders by customer. Only orders from a selected date onward are considered. The output must show order count, total net value and a status such as High Value or Standard. We will keep database access separate from calculation logic so that tests run without production data.
Fetch required fields only
Convert DB rows to model
Use modern expressions
Arrange, Act, Assert
Run ATC and review
Step 1: Define clean types
Keep the public model small and explicit. Replace the demo fields with released CDS entities and domain types available in your system.
TYPES: BEGIN OF ty_order,
customer_id TYPE kunnr,
sales_order TYPE vbeln_va,
net_value TYPE netwr,
currency TYPE waers,
END OF ty_order,
tt_order TYPE STANDARD TABLE OF ty_order WITH EMPTY KEY.
TYPES: BEGIN OF ty_summary,
customer_id TYPE kunnr,
order_count TYPE i,
total_value TYPE decfloat34,
status TYPE string,
END OF ty_summary,
tt_summary TYPE SORTED TABLE OF ty_summary
WITH UNIQUE KEY customer_id.
Step 2: Read only what you need
Modern Open SQL supports inline declarations and host variables marked with @. Avoid SELECT *; choosing only required columns reduces data transfer and makes the intent clear.
SELECT customer_id,
sales_order,
total_net_amount AS net_value,
transaction_currency AS currency
FROM i_salesorder
WHERE creation_date >= @iv_date_from
INTO TABLE @DATA(lt_orders).
IF lt_orders IS INITIAL.
RETURN.
ENDIF.
Step 3: Aggregate with GROUP BY and REDUCE
The grouping expression avoids nested loops and keeps the calculation close to the business rule. REDUCE calculates totals without a temporary accumulator variable outside the expression.
LOOP AT it_orders INTO DATA(ls_order)
GROUP BY ( customer_id = ls_order-customer_id )
ASSIGNING FIELD-SYMBOL(<customer_group>).
DATA(lv_total) = REDUCE decfloat34(
INIT total = CONV decfloat34( 0 )
FOR order IN GROUP <customer_group>
NEXT total = total + order-net_value ).
INSERT VALUE #(
customer_id = <customer_group>-customer_id
order_count = lines( VALUE tt_order(
FOR row IN GROUP <customer_group> ( row ) ) )
total_value = lv_total
status = COND string(
WHEN lv_total >= 100000 THEN 'High Value'
ELSE 'Standard' ) )
INTO TABLE rt_summary.
ENDLOOP.
Step 4: Put logic behind a testable class
CLASS zcl_sales_summary DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
METHODS summarize
IMPORTING it_orders TYPE tt_order
RETURNING VALUE(rt_summary) TYPE tt_summary.
ENDCLASS.
CLASS zcl_sales_summary IMPLEMENTATION.
METHOD summarize.
"Grouping implementation from Step 3
ENDMETHOD.
ENDCLASS.
Separating the calculation from the repository/database layer is the key design decision. The calculation now accepts plain internal-table data, which makes the unit test fast and deterministic.
Step 5: ABAP Unit test
A local test class is declared with FOR TESTING. The test follows Arrange–Act–Assert and verifies both the numeric result and the business classification.
CLASS ltc_sales_summary DEFINITION FINAL
FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
PRIVATE SECTION.
METHODS totals_are_grouped FOR TESTING.
ENDCLASS.
CLASS ltc_sales_summary IMPLEMENTATION.
METHOD totals_are_grouped.
DATA(lt_orders) = VALUE tt_order(
( customer_id = '1000' sales_order = '1'
net_value = '60000' currency = 'INR' )
( customer_id = '1000' sales_order = '2'
net_value = '50000' currency = 'INR' ) ).
DATA(lt_result) = NEW zcl_sales_summary( )->summarize( lt_orders ).
DATA(ls_result) = lt_result[ customer_id = '1000' ].
cl_abap_unit_assert=>assert_equals(
act = ls_result-order_count exp = 2 ).
cl_abap_unit_assert=>assert_equals(
act = ls_result-total_value exp = '110000' ).
cl_abap_unit_assert=>assert_equals(
act = ls_result-status exp = 'High Value' ).
ENDMETHOD.
ENDCLASS.
Add these negative tests
- Empty input returns an empty result.
- A customer below the threshold gets Standard.
- Two customers produce two independent summary rows.
- Boundary value 100000 follows the documented rule.
- Different currencies are rejected or converted before aggregation.
ATC and quality checklist
- No unnecessary database columns or database access inside loops.
- Released APIs/CDS views are used for the target system.
- Authorization, client handling and currency semantics are reviewed.
- Method names describe intent; methods remain small.
- Every business rule has at least one positive and one negative test.
- Run local ATC checks and correct findings before transport.
Hands-on exercise
- Add a parameter
iv_high_value_limitinstead of hard-coding 100000. - Add an order_count rule: label a customer Frequent when count is 5 or more.
- Create tests for 0, 1, 4 and 5 orders.
- Measure whether aggregation remains correct when the input order changes.
- Run ATC and document one improvement you made.
Interview revision
Q1. Why are inline declarations useful?
They keep a variable close to the statement that defines its type and value, reducing boilerplate. Use explicit types when an API contract or numeric precision must be obvious.
Q2. VALUE vs CORRESPONDING?VALUE constructs a value directly. CORRESPONDING maps components with matching names and supports mapping rules.
Q3. When should REDUCE be avoided?
Avoid it when the expression becomes difficult to debug or mixes unrelated rules. Readability is more important than using the newest syntax.
Q4. Why separate database access from calculation?
It improves testability, reuse and maintainability. Unit tests can run with controlled data and no database dependency.
Q5. What makes an ABAP Unit test reliable?
It is isolated, deterministic, fast, expressive and verifies one clearly defined behaviour.
Quick course revision map
| Area | Modern ABAP tool | Use it for |
|---|---|---|
| Declarations | DATA( ), FIELD-SYMBOL( ) | Less boilerplate |
| Construction | VALUE, NEW, CONV, CORRESPONDING | Create and map values |
| Decisions | COND, SWITCH | Expression-based rules |
| Tables | FOR, FILTER, REDUCE, GROUP BY | Transform and aggregate |
| Quality | ABAP Unit, ATC, Clean ABAP | Safe maintainable code |
Official learning references
- SAP ABAP Keyword Documentation: CLASS FOR TESTING
- SAP Help: How to Write ABAP Unit Test Classes
- SAP Clean ABAP Style Guide
Your turn: Complete the exercise, run the unit tests, and share which legacy loop you would refactor first.
0 Comments