Unit Tests para Servicios RAP: Probar Validaciones, Actions y Determinations

Kategorie
RAP
Veröffentlicht
Autor
Johannes

Los Unit Tests son indispensables para servicios RAP. Este tutorial muestra cómo probar sistemáticamente Validaciones, Actions y Determinations, con ejemplos de código completos del escenario de reservas de vuelo.

Por qué Probar Servicios RAP?

Los servicios RAP contienen lógica de negocio crítica:

  • Validaciones: Verifican la consistencia de datos antes de guardar
  • Actions: Ejecutan cambios de estado y procesos de negocio
  • Determinations: Calculan valores automáticamente

Un error en estos componentes puede llevar a inconsistencias de datos o fallos en procesos. Los Unit Tests detectan estos errores tempranamente.

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

Configuración de Test para RAP

Estructura Básica de una Clase de Test RAP

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.
" Métodos de test para Validaciones
METHODS test_validate_flight_date_ok FOR TESTING.
METHODS test_validate_flight_date_past FOR TESTING.
METHODS test_validate_passenger_name FOR TESTING.
" Métodos de test para Actions
METHODS test_confirm_booking FOR TESTING.
METHODS test_cancel_booking FOR TESTING.
" Métodos de test para Determinations
METHODS test_set_initial_status FOR TESTING.
METHODS test_calculate_total_price FOR TESTING.
ENDCLASS.

Configurar CDS Test Environment

CLASS ltc_booking IMPLEMENTATION.
METHOD class_setup.
" CDS Test Environment para todas las entidades involucradas
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.
" Antes de cada test: Restablecer datos de prueba
mo_environment->clear_doubles( ).
" Insertar datos de prueba dependientes (Vuelos, Pasajeros)
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.
" Después de cada test: Revertir transacciones
ROLLBACK ENTITIES.
ENDMETHOD.
ENDCLASS.

Probar Validaciones

Las validaciones verifican reglas de negocio y llenan FAILED y REPORTED en caso de errores.

Test: Fecha de Vuelo Válida

METHOD test_validate_flight_date_ok.
" GIVEN: Reserva con fecha de vuelo en el futuro
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: Crear reserva y hacer commit
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: No se esperan errores
cl_abap_unit_assert=>assert_initial(
act = commit_failed-flightbooking
msg = 'Reserva con fecha válida debería ser exitosa'
).
" Reserva fue creada
cl_abap_unit_assert=>assert_not_initial(
act = mapped-flightbooking
msg = 'Reserva debería haber sido creada'
).
ENDMETHOD.

Test: Fecha de Vuelo en el Pasado

METHOD test_validate_flight_date_past.
" GIVEN: Reserva con fecha de vuelo en el pasado
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: Crear reserva
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: La validación debería fallar
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-flightbooking
msg = 'Reserva con fecha pasada debería fallar'
).
" Debería haber un mensaje de error
cl_abap_unit_assert=>assert_not_initial(
act = commit_reported-flightbooking
msg = 'Debería existir un mensaje de error'
).
" El campo FlightDate debería estar marcado
cl_abap_unit_assert=>assert_equals(
exp = if_abap_behv=>mk-on
act = commit_reported-flightbooking[ 1 ]-%element-FlightDate
msg = 'El campo FlightDate debería estar marcado como erróneo'
).
ENDMETHOD.

Test: Validación de Campo Obligatorio

METHOD test_validate_passenger_name.
" GIVEN: Reserva sin PassengerId
DATA lt_create TYPE TABLE FOR CREATE zi_flightbooking.
lt_create = VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = '' " Vacío - ¡Campo obligatorio!
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: Error de campo obligatorio
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-flightbooking
msg = 'Pasajero faltante debería causar error'
).
" Campo PassengerId marcado
cl_abap_unit_assert=>assert_equals(
exp = if_abap_behv=>mk-on
act = commit_reported-flightbooking[ 1 ]-%element-PassengerId
msg = 'El campo PassengerId debería estar marcado'
).
ENDMETHOD.

Probar Actions

Las Actions ejecutan lógica de negocio y cambian el estado de las entidades.

Test: Confirmar Reserva

METHOD test_confirm_booking.
" GIVEN: Crear reserva abierta
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 de la reserva creada
DATA(lv_booking_uuid) = mapped_create-flightbooking[ 1 ]-BookingUUID.
" WHEN: Ejecutar action confirmBooking
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: Sin error
cl_abap_unit_assert=>assert_initial(
act = commit_failed-flightbooking
msg = 'confirmBooking debería ser exitoso'
).
" Verificar estado
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 = 'El estado debería estar en Confirmed (C)'
).
ENDMETHOD.

Test: Cancelar Reserva

METHOD test_cancel_booking.
" GIVEN: Reserva confirmada
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 con motivo
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
EXECUTE cancelBooking FROM VALUE #(
( BookingUUID = lv_booking_uuid
%param = VALUE za_cancellation_reason(
ReasonCode = 'CX'
ReasonText = 'Cliente ha cancelado' ) )
)
FAILED DATA(failed)
REPORTED DATA(reported).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed).
" THEN: Sin error
cl_abap_unit_assert=>assert_initial(
act = commit_failed-flightbooking
msg = 'cancelBooking debería ser exitoso'
).
" Verificar estado y motivo de cancelación
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 = 'El estado debería estar en Cancelled (X)'
).
cl_abap_unit_assert=>assert_equals(
exp = 'Cliente ha cancelado'
act = lt_booking[ 1 ]-CancellationReason
msg = 'El motivo de cancelación debería estar guardado'
).
ENDMETHOD.

Test: Action con Caso de Error

METHOD test_confirm_already_confirmed.
" GIVEN: Reserva ya confirmada
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM VALUE #(
( %cid = 'B1'
FlightId = 'LH400'
PassengerId = 'P001'
Status = 'C' ) " Ya Confirmed
)
MAPPED DATA(mapped_create).
COMMIT ENTITIES.
DATA(lv_booking_uuid) = mapped_create-flightbooking[ 1 ]-BookingUUID.
" WHEN: Intentar confirmBooking nuevamente
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: Se espera error
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-flightbooking
msg = 'Confirmación duplicada debería fallar'
).
" Verificar que existe mensaje de error
cl_abap_unit_assert=>assert_bound(
act = commit_reported-flightbooking[ 1 ]-%msg
msg = 'Debería existir un mensaje de error'
).
ENDMETHOD.

Probar Determinations

Las Determinations establecen valores de campo automáticamente. Se ejecutan en on modify o on save.

Test: Establecer Estado Inicial

METHOD test_set_initial_status.
" GIVEN: Nueva reserva sin estado
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 NO se establece - La Determination debería hacerlo
)
).
" WHEN: Crear reserva
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM lt_create
MAPPED DATA(mapped).
COMMIT ENTITIES.
" THEN: La Determination debería haber establecido Status en 'O' (Open)
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 = 'La Determination debería establecer Status en Open'
).
" CreatedBy debería estar lleno
cl_abap_unit_assert=>assert_not_initial(
act = lt_booking[ 1 ]-CreatedBy
msg = 'CreatedBy debería estar establecido por la Determination'
).
" CreatedAt debería estar lleno
cl_abap_unit_assert=>assert_not_initial(
act = lt_booking[ 1 ]-CreatedAt
msg = 'CreatedAt debería estar establecido por la Determination'
).
ENDMETHOD.

Test: Calcular Precio Total

METHOD test_calculate_total_price.
" GIVEN: Reserva con precio base y 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 piezas de equipaje extra
MealOption = 'P' " Premium Meal
)
).
" WHEN
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
CREATE FROM lt_create
MAPPED DATA(mapped).
COMMIT ENTITIES.
" THEN: TotalPrice debería estar calculado
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 debería ser { lv_expected_total }|
).
ENDMETHOD.

Test: Determination en Cambio de Campo

METHOD test_recalculate_on_change.
" GIVEN: Reserva existente
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.
" TotalPrice inicial
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: Cambiar SeatClass a Business
MODIFY ENTITIES OF zi_flightbooking
ENTITY FlightBooking
UPDATE FIELDS ( SeatClass )
WITH VALUE #(
( BookingUUID = lv_booking_uuid
SeatClass = 'C' ) " Business Class
).
COMMIT ENTITIES.
" THEN: TotalPrice debería estar recalculado
READ ENTITIES OF zi_flightbooking
ENTITY FlightBooking
FIELDS ( TotalPrice )
WITH VALUE #( ( BookingUUID = lv_booking_uuid ) )
RESULT DATA(lt_updated).
" Business Class es más cara que Economy
cl_abap_unit_assert=>assert_true(
act = xsdbool( lt_updated[ 1 ]-TotalPrice > lv_initial_price )
msg = 'TotalPrice debería ser mayor después del upgrade'
).
ENDMETHOD.

Mocking de Dependencies

Test con Datos de Vuelo Simulados

METHOD test_with_mocked_flight.
" CDS Test Environment permite insertar datos de prueba directamente
" Esto reemplaza la tabla real de la base de datos
" Vuelo especial para este 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: Reserva para vuelo simulado
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: Exitoso con datos simulados
cl_abap_unit_assert=>assert_initial(
act = commit_failed-flightbooking
msg = 'Reserva con vuelo simulado debería funcionar'
).
ENDMETHOD.

Test: Vuelo Agotado

METHOD test_fully_booked_flight.
" GIVEN: Vuelo agotado (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 ) " ¡Agotado!
)
).
mo_environment->insert_test_data(
i_data = VALUE zi_passenger_tab(
( PassengerId = 'P001' FirstName = 'Max' LastName = 'Mustermann' )
)
).
" WHEN: Intentar reservar en vuelo agotado
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: Error - Vuelo agotado
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-flightbooking
msg = 'Vuelo agotado debería causar error'
).
ENDMETHOD.

Ejemplo Completo de Clase de Test

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.
" === VALIDACIONES ===
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.
" === MÉTODOS AUXILIARES ===
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( ).
" Datos de prueba estándar
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.
" === MÉTODO AUXILIAR ===
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.
" === VALIDACIONES ===
METHOD test_valid_booking.
" GIVEN/WHEN
DATA(lv_uuid) = create_test_booking( ).
" THEN
cl_abap_unit_assert=>assert_not_initial(
act = lv_uuid
msg = 'Reserva válida debería ser creada'
).
ENDMETHOD.
METHOD test_invalid_date.
" GIVEN: Fecha en el pasado
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 = 'Fecha pasada debería causar error'
).
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 = 'Pasajero faltante debería causar error'
).
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' ) " Inválido
).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed).
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-flightbooking
msg = 'SeatClass inválido debería causar error'
).
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 debería ser exitoso'
).
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 = 'Cancelación de prueba' ) )
).
COMMIT ENTITIES
RESPONSE OF zi_flightbooking
FAILED DATA(commit_failed).
cl_abap_unit_assert=>assert_initial(
act = commit_failed-flightbooking
msg = 'cancelBooking debería ser exitoso'
).
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 = 'Reserva cancelada no puede ser confirmada'
).
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 = 'Reprogramación debería ser exitosa'
).
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 = 'Estado inicial debería ser Open (O)'
).
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 debería estar establecido por la Determination'
).
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 debería generarse automáticamente'
).
ENDMETHOD.
ENDCLASS.

Mejores Prácticas para Tests RAP

Lista de Verificación de Tests

ComponenteQué probar?Cómo probar?
ValidationCaso exitosoCREATE con datos válidos
Caso de errorCREATE con datos inválidos
Marcación de campoVerificar %element-FieldName
Mensaje de errorVerificar %msg
ActionCaso exitosoEXECUTE + verificar estado
Caso de errorEXECUTE en estado inválido
Con parámetroPasar %param
Determinationon modifyVerificar campo después de CREATE
on saveVerificar campo después de COMMIT
RecálculoUPDATE + cambio de campo

Lo que Sí y lo que No

SÍ:

" Preparar datos de prueba en setup()
METHOD setup.
mo_environment->clear_doubles( ).
mo_environment->insert_test_data( ... ).
ENDMETHOD.
" ROLLBACK ENTITIES en teardown()
METHOD teardown.
ROLLBACK ENTITIES.
ENDMETHOD.
" Un test = Un escenario
METHOD test_confirm_booking.
" Solo probar confirmBooking
ENDMETHOD.
" Métodos auxiliares para reutilización
METHOD create_test_booking.
" Creación de reserva reutilizable
ENDMETHOD.

NO:

" No usar datos reales de BD en tests
SELECT * FROM zflight INTO TABLE @lt_flights. " ¡No!
" No dependencias entre tests
METHOD test_2.
" Espera que test_1 ya haya ejecutado - ¡MAL!
ENDMETHOD.
" No demasiadas assertions por test
METHOD test_everything.
cl_abap_unit_assert=>... " 20 assertions = confuso
ENDMETHOD.

Recursos Adicionales