Unit Tests für RAP Services: Validierungen, Actions und Determinations testen

Kategorie
RAP
Veröffentlicht
Autor
Johannes

Unit Tests sind für RAP Services unverzichtbar. Dieses Tutorial zeigt, wie du Validierungen, Actions und Determinations systematisch testest – mit vollständigen Code-Beispielen aus dem Flugbuchungs-Szenario.

Warum RAP Services testen?

RAP Services enthalten kritische Geschäftslogik:

  • Validierungen: Prüfen Datenkonsistenz vor dem Speichern
  • Actions: Führen Statuswechsel und Geschäftsprozesse aus
  • Determinations: Berechnen automatisch Werte

Ein Fehler in diesen Komponenten kann zu Dateninkonsistenzen oder Prozessfehlern führen. Unit Tests fangen diese Fehler frühzeitig ab.

┌─────────────────────────────────────────────────────────────┐
│ RAP Business Object │
├─────────────────────────────────────────────────────────────┤
│ ┌───────────────┐ ┌───────────────┐ ┌─────────────────┐ │
│ │ Determination │ │ Validation │ │ Action │ │
│ │ setDefaults() │ │ validateDate()│ │ confirmBooking()│ │
│ └───────┬───────┘ └───────┬───────┘ └────────┬────────┘ │
│ │ │ │ │
│ └──────────────────┼────────────────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Unit Tests │ │
│ │ ltc_booking │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘

Test-Setup für RAP

Grundstruktur einer RAP Test-Klasse

CLASS ltc_booking DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA mo_environment TYPE REF TO if_cds_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS setup.
METHODS teardown.
" Test-Methoden für Validierungen
METHODS test_validate_flight_date_ok FOR TESTING.
METHODS test_validate_flight_date_past FOR TESTING.
METHODS test_validate_passenger_name FOR TESTING.
" Test-Methoden für Actions
METHODS test_confirm_booking FOR TESTING.
METHODS test_cancel_booking FOR TESTING.
" Test-Methoden für Determinations
METHODS test_set_initial_status FOR TESTING.
METHODS test_calculate_total_price FOR TESTING.
ENDCLASS.

CDS Test Environment einrichten

CLASS ltc_booking IMPLEMENTATION.
METHOD class_setup.
" CDS Test Environment für alle beteiligten Entitäten
mo_environment = cl_cds_test_environment=>create_for_multiple_cds(
i_for_entities = VALUE #(
( i_for_entity = 'ZI_FlightBooking' )
( i_for_entity = 'ZI_Flight' )
( i_for_entity = 'ZI_Passenger' )
)
).
ENDMETHOD.
METHOD class_teardown.
mo_environment->destroy( ).
ENDMETHOD.
METHOD setup.
" Vor jedem Test: Testdaten zurücksetzen
mo_environment->clear_doubles( ).
" Abhängige Testdaten einfügen (Flüge, Passagiere)
mo_environment->insert_test_data(
i_data = VALUE zi_flight_tab(
( FlightId = 'LH400'
Carrier = 'LH'
Connection = '0400'
FlightDate = cl_abap_context_info=>get_system_date( ) + 30
Price = '450.00'
Currency = 'EUR'
SeatsMax = 200
SeatsOccupied = 50 )
( FlightId = 'LH401'
Carrier = 'LH'
Connection = '0401'
FlightDate = cl_abap_context_info=>get_system_date( ) - 10
Price = '380.00'
Currency = 'EUR'
SeatsMax = 200
SeatsOccupied = 200 )
)
).
mo_environment->insert_test_data(
i_data = VALUE zi_passenger_tab(
( PassengerId = 'P001' FirstName = 'Max' LastName = 'Mustermann' )
( PassengerId = 'P002' FirstName = 'Lisa' LastName = 'Beispiel' )
)
).
ENDMETHOD.
METHOD teardown.
" Nach jedem Test: Transaktionen zurückrollen
ROLLBACK ENTITIES.
ENDMETHOD.
ENDCLASS.

Validierungen testen

Validierungen prüfen Geschäftsregeln und füllen FAILED und REPORTED bei Fehlern.

Test: Gültiges Flugdatum

METHOD test_validate_flight_date_ok.
" GIVEN: Buchung mit Flugdatum in der Zukunft
DATA lt_create TYPE TABLE FOR CREATE zi_flightbooking.
lt_create = VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = 'P001'
FlightDate = cl_abap_context_info=>get_system_date( ) + 30
SeatClass = 'Y'
Price = '450.00'
Currency = 'EUR' )
).
" WHEN: Buchung erstellen und committen
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM lt_create
MAPPED DATA(mapped)
FAILED DATA(failed)
REPORTED DATA(reported).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed)
REPORTED DATA(commit_reported).
" THEN: Keine Fehler erwartet
cl_abap_unit_assert=>assert_initial(
act = commit_failed-flightbooking
msg = 'Buchung mit gültigem Datum sollte erfolgreich sein'
).
" Buchung wurde erstellt
cl_abap_unit_assert=>assert_not_initial(
act = mapped-flightbooking
msg = 'Buchung sollte erstellt worden sein'
).
ENDMETHOD.

Test: Flugdatum in der Vergangenheit

METHOD test_validate_flight_date_past.
" GIVEN: Buchung mit Flugdatum in der Vergangenheit
DATA lt_create TYPE TABLE FOR CREATE zi_flightbooking.
lt_create = VALUE #(
( %cid = 'B1'
FlightId = 'LH401'
PassengerId = 'P001'
FlightDate = cl_abap_context_info=>get_system_date( ) - 10
SeatClass = 'Y' )
).
" WHEN: Buchung erstellen
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM lt_create
FAILED DATA(failed).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed)
REPORTED DATA(commit_reported).
" THEN: Validierung sollte fehlschlagen
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-flightbooking
msg = 'Buchung mit vergangenem Datum sollte fehlschlagen'
).
" Fehlermeldung sollte vorhanden sein
cl_abap_unit_assert=>assert_not_initial(
act = commit_reported-flightbooking
msg = 'Fehlermeldung sollte vorhanden sein'
).
" FlightDate-Feld sollte markiert sein
cl_abap_unit_assert=>assert_equals(
exp = if_abap_behv=>mk-on
act = commit_reported-flightbooking[ 1 ]-%element-FlightDate
msg = 'FlightDate-Feld sollte als fehlerhaft markiert sein'
).
ENDMETHOD.

Test: Pflichtfeld-Validierung

METHOD test_validate_passenger_name.
" GIVEN: Buchung ohne PassengerId
DATA lt_create TYPE TABLE FOR CREATE zi_flightbooking.
lt_create = VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = '' " Leer - Pflichtfeld!
FlightDate = cl_abap_context_info=>get_system_date( ) + 30
SeatClass = 'Y' )
).
" WHEN
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM lt_create.
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed)
REPORTED DATA(commit_reported).
" THEN: Pflichtfeld-Fehler
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-flightbooking
msg = 'Fehlender Passagier sollte Fehler verursachen'
).
" PassengerId-Feld markiert
cl_abap_unit_assert=>assert_equals(
exp = if_abap_behv=>mk-on
act = commit_reported-flightbooking[ 1 ]-%element-PassengerId
msg = 'PassengerId-Feld sollte markiert sein'
).
ENDMETHOD.

Actions testen

Actions führen Geschäftslogik aus und ändern den Zustand von Entitäten.

Test: Buchung bestätigen

METHOD test_confirm_booking.
" GIVEN: Offene Buchung anlegen
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = 'P001'
FlightDate = cl_abap_context_info=>get_system_date( ) + 30
Status = 'O' " Open
SeatClass = 'Y' )
)
MAPPED DATA(mapped_create).
COMMIT ENTITIES.
" Key der erstellten Buchung
DATA(lv_booking_uuid) = mapped_create-flightbooking[ 1 ]-BookingUUID.
" WHEN: Action confirmBooking ausführen
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
EXECUTE confirmBooking FROM VALUE #(
( BookingUUID = lv_booking_uuid )
)
RESULT DATA(result)
FAILED DATA(failed)
REPORTED DATA(reported).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed).
" THEN: Kein Fehler
cl_abap_unit_assert=>assert_initial(
act = commit_failed-flightbooking
msg = 'confirmBooking sollte erfolgreich sein'
).
" Status prüfen
READ ENTITIES OF zi_flightbooking
ENTITY FlightBooking
FIELDS ( Status )
WITH VALUE #( ( BookingUUID = lv_booking_uuid ) )
RESULT DATA(lt_booking).
cl_abap_unit_assert=>assert_equals(
exp = 'C' " Confirmed
act = lt_booking[ 1 ]-Status
msg = 'Status sollte auf Confirmed (C) gesetzt sein'
).
ENDMETHOD.

Test: Buchung stornieren

METHOD test_cancel_booking.
" GIVEN: Bestätigte Buchung
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = 'P001'
FlightDate = cl_abap_context_info=>get_system_date( ) + 30
Status = 'C' " Confirmed
SeatClass = 'Y' )
)
MAPPED DATA(mapped_create).
COMMIT ENTITIES.
DATA(lv_booking_uuid) = mapped_create-flightbooking[ 1 ]-BookingUUID.
" WHEN: Action cancelBooking mit Grund
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
EXECUTE cancelBooking FROM VALUE #(
( BookingUUID = lv_booking_uuid
%param = VALUE za_cancellation_reason(
ReasonCode = 'CX'
ReasonText = 'Kunde hat storniert' ) )
)
FAILED DATA(failed)
REPORTED DATA(reported).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed).
" THEN: Kein Fehler
cl_abap_unit_assert=>assert_initial(
act = commit_failed-flightbooking
msg = 'cancelBooking sollte erfolgreich sein'
).
" Status und Stornierungsgrund prüfen
READ ENTITIES OF zi_flightbooking
ENTITY FlightBooking
FIELDS ( Status CancellationReason )
WITH VALUE #( ( BookingUUID = lv_booking_uuid ) )
RESULT DATA(lt_booking).
cl_abap_unit_assert=>assert_equals(
exp = 'X' " Cancelled
act = lt_booking[ 1 ]-Status
msg = 'Status sollte auf Cancelled (X) gesetzt sein'
).
cl_abap_unit_assert=>assert_equals(
exp = 'Kunde hat storniert'
act = lt_booking[ 1 ]-CancellationReason
msg = 'Stornierungsgrund sollte gespeichert sein'
).
ENDMETHOD.

Test: Action mit Fehlerfall

METHOD test_confirm_already_confirmed.
" GIVEN: Bereits bestätigte Buchung
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = 'P001'
Status = 'C' ) " Bereits Confirmed
)
MAPPED DATA(mapped_create).
COMMIT ENTITIES.
DATA(lv_booking_uuid) = mapped_create-flightbooking[ 1 ]-BookingUUID.
" WHEN: Erneut confirmBooking versuchen
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
EXECUTE confirmBooking FROM VALUE #(
( BookingUUID = lv_booking_uuid )
)
FAILED DATA(failed)
REPORTED DATA(reported).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed)
REPORTED DATA(commit_reported).
" THEN: Fehler erwartet
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-flightbooking
msg = 'Doppelte Bestätigung sollte fehlschlagen'
).
" Prüfen ob Fehlermeldung vorhanden
cl_abap_unit_assert=>assert_bound(
act = commit_reported-flightbooking[ 1 ]-%msg
msg = 'Fehlermeldung sollte vorhanden sein'
).
ENDMETHOD.

Determinations testen

Determinations setzen automatisch Feldwerte. Sie laufen bei on modify oder on save.

Test: Initialstatus setzen

METHOD test_set_initial_status.
" GIVEN: Neue Buchung ohne Status
DATA lt_create TYPE TABLE FOR CREATE zi_flightbooking.
lt_create = VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = 'P001'
FlightDate = cl_abap_context_info=>get_system_date( ) + 30
SeatClass = 'Y'
" Status wird NICHT gesetzt - Determination soll das machen
)
).
" WHEN: Buchung erstellen
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM lt_create
MAPPED DATA(mapped).
COMMIT ENTITIES.
" THEN: Determination sollte Status auf 'O' (Open) gesetzt haben
READ ENTITIES OF zi_flightbooking
ENTITY FlightBooking
FIELDS ( Status CreatedBy CreatedAt )
WITH VALUE #( ( %cid = 'B1' ) )
RESULT DATA(lt_booking).
cl_abap_unit_assert=>assert_equals(
exp = 'O' " Open
act = lt_booking[ 1 ]-Status
msg = 'Determination sollte Status auf Open setzen'
).
" CreatedBy sollte gefüllt sein
cl_abap_unit_assert=>assert_not_initial(
act = lt_booking[ 1 ]-CreatedBy
msg = 'CreatedBy sollte durch Determination gesetzt sein'
).
" CreatedAt sollte gefüllt sein
cl_abap_unit_assert=>assert_not_initial(
act = lt_booking[ 1 ]-CreatedAt
msg = 'CreatedAt sollte durch Determination gesetzt sein'
).
ENDMETHOD.

Test: Gesamtpreis berechnen

METHOD test_calculate_total_price.
" GIVEN: Buchung mit Basispreis und Extras
DATA lt_create TYPE TABLE FOR CREATE zi_flightbooking.
lt_create = VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = 'P001'
FlightDate = cl_abap_context_info=>get_system_date( ) + 30
SeatClass = 'C' " Business Class
BasePrice = '450.00'
Currency = 'EUR'
ExtraLuggage = 2 " 2 Gepäckstücke extra
MealOption = 'P' " Premium Meal
)
).
" WHEN
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM lt_create
MAPPED DATA(mapped).
COMMIT ENTITIES.
" THEN: TotalPrice sollte berechnet sein
READ ENTITIES OF zi_flightbooking
ENTITY FlightBooking
FIELDS ( TotalPrice BasePrice )
WITH VALUE #( ( %cid = 'B1' ) )
RESULT DATA(lt_booking).
" Business Class: BasePrice * 2.5 = 1125.00
" Extra Luggage: 2 * 50 = 100.00
" Premium Meal: 45.00
" Total: 1270.00
DATA(lv_expected_total) = CONV wrbtr( '1270.00' ).
cl_abap_unit_assert=>assert_equals(
exp = lv_expected_total
act = lt_booking[ 1 ]-TotalPrice
msg = |TotalPrice sollte { lv_expected_total } sein|
).
ENDMETHOD.

Test: Determination bei Feldänderung

METHOD test_recalculate_on_change.
" GIVEN: Existierende Buchung
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = 'P001'
SeatClass = 'Y' " Economy
BasePrice = '450.00'
Currency = 'EUR' )
)
MAPPED DATA(mapped_create).
COMMIT ENTITIES.
DATA(lv_booking_uuid) = mapped_create-flightbooking[ 1 ]-BookingUUID.
" Initialer TotalPrice
READ ENTITIES OF zi_flightbooking
ENTITY FlightBooking
FIELDS ( TotalPrice )
WITH VALUE #( ( BookingUUID = lv_booking_uuid ) )
RESULT DATA(lt_initial).
DATA(lv_initial_price) = lt_initial[ 1 ]-TotalPrice.
" WHEN: SeatClass auf Business ändern
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
UPDATE FIELDS ( SeatClass )
WITH VALUE #(
( BookingUUID = lv_booking_uuid
SeatClass = 'C' ) " Business Class
).
COMMIT ENTITIES.
" THEN: TotalPrice sollte neu berechnet sein
READ ENTITIES OF zi_flightbooking
ENTITY FlightBooking
FIELDS ( TotalPrice )
WITH VALUE #( ( BookingUUID = lv_booking_uuid ) )
RESULT DATA(lt_updated).
" Business Class ist teurer als Economy
cl_abap_unit_assert=>assert_true(
act = xsdbool( lt_updated[ 1 ]-TotalPrice > lv_initial_price )
msg = 'TotalPrice sollte nach Upgrade höher sein'
).
ENDMETHOD.

Mocking von Dependencies

Test mit gemockten Flugdaten

METHOD test_with_mocked_flight.
" CDS Test Environment erlaubt direktes Einfügen von Testdaten
" Das ersetzt die echte Datenbanktabelle
" Spezieller Flug für diesen Test
mo_environment->insert_test_data(
i_data = VALUE zi_flight_tab(
( FlightId = 'TEST01'
Carrier = 'XX'
FlightDate = cl_abap_context_info=>get_system_date( ) + 100
Price = '999.99'
Currency = 'EUR'
SeatsMax = 10
SeatsOccupied = 0 )
)
).
" WHEN: Buchung für gemockten Flug
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM VALUE #(
( %cid = 'B1'
FlightId = 'TEST01'
PassengerId = 'P001'
SeatClass = 'Y' )
)
MAPPED DATA(mapped).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed).
" THEN: Erfolgreich mit gemockten Daten
cl_abap_unit_assert=>assert_initial(
act = commit_failed-flightbooking
msg = 'Buchung mit gemocktem Flug sollte funktionieren'
).
ENDMETHOD.

Test: Ausgebuchter Flug

METHOD test_fully_booked_flight.
" GIVEN: Ausgebuchter Flug (SeatsOccupied = SeatsMax)
mo_environment->clear_doubles( ).
mo_environment->insert_test_data(
i_data = VALUE zi_flight_tab(
( FlightId = 'FULL01'
Carrier = 'LH'
FlightDate = cl_abap_context_info=>get_system_date( ) + 30
SeatsMax = 100
SeatsOccupied = 100 ) " Ausgebucht!
)
).
mo_environment->insert_test_data(
i_data = VALUE zi_passenger_tab(
( PassengerId = 'P001' FirstName = 'Max' LastName = 'Mustermann' )
)
).
" WHEN: Buchung für ausgebuchten Flug versuchen
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM VALUE #(
( %cid = 'B1'
FlightId = 'FULL01'
PassengerId = 'P001'
SeatClass = 'Y' )
).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed)
REPORTED DATA(commit_reported).
" THEN: Fehler - Flug ausgebucht
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-flightbooking
msg = 'Ausgebuchter Flug sollte Fehler verursachen'
).
ENDMETHOD.

Vollständiges Testklassen-Beispiel

CLASS ltc_flight_booking DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA mo_environment TYPE REF TO if_cds_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS setup.
METHODS teardown.
" === VALIDIERUNGEN ===
METHODS test_valid_booking FOR TESTING.
METHODS test_invalid_date FOR TESTING.
METHODS test_missing_passenger FOR TESTING.
METHODS test_invalid_seat_class FOR TESTING.
" === ACTIONS ===
METHODS test_confirm_booking FOR TESTING.
METHODS test_cancel_booking FOR TESTING.
METHODS test_confirm_already_canceled FOR TESTING.
METHODS test_reschedule_booking FOR TESTING.
" === DETERMINATIONS ===
METHODS test_set_initial_status FOR TESTING.
METHODS test_calculate_price FOR TESTING.
METHODS test_set_booking_number FOR TESTING.
" === HILFSMETHODEN ===
METHODS create_test_booking
IMPORTING iv_cid TYPE string DEFAULT 'B1'
iv_flight_id TYPE zflight_id DEFAULT 'LH400'
iv_status TYPE zstatus DEFAULT 'O'
RETURNING VALUE(rv_uuid) TYPE sysuuid_x16.
ENDCLASS.
CLASS ltc_flight_booking IMPLEMENTATION.
METHOD class_setup.
mo_environment = cl_cds_test_environment=>create_for_multiple_cds(
i_for_entities = VALUE #(
( i_for_entity = 'ZI_FlightBooking' )
( i_for_entity = 'ZI_Flight' )
( i_for_entity = 'ZI_Passenger' )
)
).
ENDMETHOD.
METHOD class_teardown.
mo_environment->destroy( ).
ENDMETHOD.
METHOD setup.
mo_environment->clear_doubles( ).
" Standard-Testdaten
mo_environment->insert_test_data(
i_data = VALUE zi_flight_tab(
( FlightId = 'LH400'
Carrier = 'LH'
Connection = '0400'
FlightDate = cl_abap_context_info=>get_system_date( ) + 30
Price = '450.00'
Currency = 'EUR'
SeatsMax = 200
SeatsOccupied = 50 )
)
).
mo_environment->insert_test_data(
i_data = VALUE zi_passenger_tab(
( PassengerId = 'P001' FirstName = 'Max' LastName = 'Mustermann' )
)
).
ENDMETHOD.
METHOD teardown.
ROLLBACK ENTITIES.
ENDMETHOD.
" === HILFSMETHODE ===
METHOD create_test_booking.
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM VALUE #(
( %cid = iv_cid
FlightId = iv_flight_id
PassengerId = 'P001'
FlightDate = cl_abap_context_info=>get_system_date( ) + 30
Status = iv_status
SeatClass = 'Y' )
)
MAPPED DATA(mapped).
COMMIT ENTITIES.
rv_uuid = mapped-flightbooking[ 1 ]-BookingUUID.
ENDMETHOD.
" === VALIDIERUNGEN ===
METHOD test_valid_booking.
" GIVEN/WHEN
DATA(lv_uuid) = create_test_booking( ).
" THEN
cl_abap_unit_assert=>assert_not_initial(
act = lv_uuid
msg = 'Gültige Buchung sollte erstellt werden'
).
ENDMETHOD.
METHOD test_invalid_date.
" GIVEN: Datum in der Vergangenheit
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = 'P001'
FlightDate = cl_abap_context_info=>get_system_date( ) - 5
SeatClass = 'Y' )
).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed).
" THEN
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-flightbooking
msg = 'Vergangenes Datum sollte Fehler verursachen'
).
ENDMETHOD.
METHOD test_missing_passenger.
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = ''
FlightDate = cl_abap_context_info=>get_system_date( ) + 30
SeatClass = 'Y' )
).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed).
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-flightbooking
msg = 'Fehlender Passagier sollte Fehler verursachen'
).
ENDMETHOD.
METHOD test_invalid_seat_class.
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = 'P001'
FlightDate = cl_abap_context_info=>get_system_date( ) + 30
SeatClass = 'X' ) " Ungültig
).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed).
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-flightbooking
msg = 'Ungültige SeatClass sollte Fehler verursachen'
).
ENDMETHOD.
" === ACTIONS ===
METHOD test_confirm_booking.
DATA(lv_uuid) = create_test_booking( iv_status = 'O' ).
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
EXECUTE confirmBooking FROM VALUE #(
( BookingUUID = lv_uuid )
)
FAILED DATA(failed).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed).
cl_abap_unit_assert=>assert_initial(
act = commit_failed-flightbooking
msg = 'confirmBooking sollte erfolgreich sein'
).
READ ENTITIES OF zi_flightbooking
ENTITY FlightBooking
FIELDS ( Status )
WITH VALUE #( ( BookingUUID = lv_uuid ) )
RESULT DATA(lt_booking).
cl_abap_unit_assert=>assert_equals(
exp = 'C'
act = lt_booking[ 1 ]-Status
).
ENDMETHOD.
METHOD test_cancel_booking.
DATA(lv_uuid) = create_test_booking( iv_status = 'C' ).
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
EXECUTE cancelBooking FROM VALUE #(
( BookingUUID = lv_uuid
%param = VALUE za_cancellation_reason(
ReasonCode = 'CX'
ReasonText = 'Test-Stornierung' ) )
).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed).
cl_abap_unit_assert=>assert_initial(
act = commit_failed-flightbooking
msg = 'cancelBooking sollte erfolgreich sein'
).
READ ENTITIES OF zi_flightbooking
ENTITY FlightBooking
FIELDS ( Status )
WITH VALUE #( ( BookingUUID = lv_uuid ) )
RESULT DATA(lt_booking).
cl_abap_unit_assert=>assert_equals(
exp = 'X'
act = lt_booking[ 1 ]-Status
).
ENDMETHOD.
METHOD test_confirm_already_canceled.
DATA(lv_uuid) = create_test_booking( iv_status = 'X' ).
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
EXECUTE confirmBooking FROM VALUE #(
( BookingUUID = lv_uuid )
).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed).
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-flightbooking
msg = 'Stornierte Buchung kann nicht bestätigt werden'
).
ENDMETHOD.
METHOD test_reschedule_booking.
DATA(lv_uuid) = create_test_booking( ).
DATA(lv_new_date) = cl_abap_context_info=>get_system_date( ) + 60.
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
EXECUTE rescheduleBooking FROM VALUE #(
( BookingUUID = lv_uuid
%param = VALUE za_reschedule_input(
NewFlightDate = lv_new_date ) )
).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed).
cl_abap_unit_assert=>assert_initial(
act = commit_failed-flightbooking
msg = 'Umbuchung sollte erfolgreich sein'
).
READ ENTITIES OF zi_flightbooking
ENTITY FlightBooking
FIELDS ( FlightDate )
WITH VALUE #( ( BookingUUID = lv_uuid ) )
RESULT DATA(lt_booking).
cl_abap_unit_assert=>assert_equals(
exp = lv_new_date
act = lt_booking[ 1 ]-FlightDate
).
ENDMETHOD.
" === DETERMINATIONS ===
METHOD test_set_initial_status.
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = 'P001'
FlightDate = cl_abap_context_info=>get_system_date( ) + 30
SeatClass = 'Y' )
)
MAPPED DATA(mapped).
COMMIT ENTITIES.
READ ENTITIES OF zi_flightbooking
ENTITY FlightBooking
FIELDS ( Status )
WITH VALUE #( ( %cid = 'B1' ) )
RESULT DATA(lt_booking).
cl_abap_unit_assert=>assert_equals(
exp = 'O'
act = lt_booking[ 1 ]-Status
msg = 'Initial-Status sollte Open (O) sein'
).
ENDMETHOD.
METHOD test_calculate_price.
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = 'P001'
FlightDate = cl_abap_context_info=>get_system_date( ) + 30
SeatClass = 'Y'
BasePrice = '450.00'
Currency = 'EUR' )
)
MAPPED DATA(mapped).
COMMIT ENTITIES.
READ ENTITIES OF zi_flightbooking
ENTITY FlightBooking
FIELDS ( TotalPrice )
WITH VALUE #( ( %cid = 'B1' ) )
RESULT DATA(lt_booking).
cl_abap_unit_assert=>assert_not_initial(
act = lt_booking[ 1 ]-TotalPrice
msg = 'TotalPrice sollte durch Determination gesetzt sein'
).
ENDMETHOD.
METHOD test_set_booking_number.
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = 'P001'
FlightDate = cl_abap_context_info=>get_system_date( ) + 30
SeatClass = 'Y' )
)
MAPPED DATA(mapped).
COMMIT ENTITIES.
READ ENTITIES OF zi_flightbooking
ENTITY FlightBooking
FIELDS ( BookingNumber )
WITH VALUE #( ( %cid = 'B1' ) )
RESULT DATA(lt_booking).
cl_abap_unit_assert=>assert_not_initial(
act = lt_booking[ 1 ]-BookingNumber
msg = 'BookingNumber sollte automatisch generiert werden'
).
ENDMETHOD.
ENDCLASS.

Best Practices für RAP Tests

Test-Checkliste

KomponenteWas testen?Wie testen?
ValidationErfolgsfallCREATE mit gültigen Daten
FehlerfallCREATE mit ungültigen Daten
Feldmarkierung%element-FieldName prüfen
Fehlermeldung%msg prüfen
ActionErfolgsfallEXECUTE + Status prüfen
FehlerfallEXECUTE auf ungültigem Status
Mit Parameter%param übergeben
Determinationon modifyFeld nach CREATE prüfen
on saveFeld nach COMMIT prüfen
NeuberechnungUPDATE + Feldänderung

Dos and Don’ts

DO:

" ✅ Testdaten in setup() vorbereiten
METHOD setup.
mo_environment->clear_doubles( ).
mo_environment->insert_test_data( ... ).
ENDMETHOD.
" ✅ ROLLBACK ENTITIES in teardown()
METHOD teardown.
ROLLBACK ENTITIES.
ENDMETHOD.
" ✅ Ein Test = Ein Szenario
METHOD test_confirm_booking.
" Nur confirmBooking testen
ENDMETHOD.
" ✅ Hilfsmethoden für Wiederverwendung
METHOD create_test_booking.
" Wiederverwendbare Buchungserstellung
ENDMETHOD.

DON’T:

" ❌ Keine echten DB-Daten in Tests
SELECT * FROM zflight INTO TABLE @lt_flights. " Nicht!
" ❌ Keine Abhängigkeiten zwischen Tests
METHOD test_2.
" Erwartet dass test_1 bereits lief - SCHLECHT!
ENDMETHOD.
" ❌ Keine zu vielen Assertions pro Test
METHOD test_everything.
cl_abap_unit_assert=>... " 20 Assertions = unübersichtlich
ENDMETHOD.

Weitere Ressourcen