El testing unitario en ABAP Cloud sigue los mismos principios que en ABAP clásico, pero con algunas consideraciones especiales para el entorno cloud y RAP.
Estructura básica de test
CLASS ltcl_my_class DEFINITION FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION. DATA: mo_cut TYPE REF TO zcl_my_class. " Class Under Test
METHODS: setup. METHODS: test_calculate_price FOR TESTING. METHODS: test_validate_input FOR TESTING.ENDCLASS.
CLASS ltcl_my_class IMPLEMENTATION. METHOD setup. mo_cut = NEW #( ). ENDMETHOD.
METHOD test_calculate_price. " Given DATA(lv_quantity) = 10. DATA(lv_unit_price) = CONV decfloat34( '25.50' ).
" When DATA(lv_result) = mo_cut->calculate_price( iv_quantity = lv_quantity iv_unit_price = lv_unit_price ).
" Then cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '255.00' ) act = lv_result msg = 'El precio calculado es incorrecto' ). ENDMETHOD.
METHOD test_validate_input. " Given DATA(lv_invalid_input) = ''.
" When/Then - Esperar excepción TRY. mo_cut->validate( lv_invalid_input ). cl_abap_unit_assert=>fail( 'Se esperaba una excepción' ). CATCH zcx_validation_error INTO DATA(lx_error). cl_abap_unit_assert=>assert_bound( lx_error ). ENDTRY. ENDMETHOD.ENDCLASS.Test Doubles en ABAP Cloud
Interface para inyección de dependencias
" Interface para abstracción de base de datosINTERFACE zif_order_repository PUBLIC. METHODS: get_order IMPORTING iv_order_id TYPE sysuuid_x16 RETURNING VALUE(rs_order) TYPE zorder.
METHODS: save_order IMPORTING is_order TYPE zorder.ENDINTERFACE.
" Implementación realCLASS zcl_order_repository DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES: zif_order_repository.ENDCLASS.
CLASS zcl_order_repository IMPLEMENTATION. METHOD zif_order_repository~get_order. SELECT SINGLE * FROM zorders WHERE order_id = @iv_order_id INTO @rs_order. ENDMETHOD.
METHOD zif_order_repository~save_order. MODIFY zorders FROM @is_order. ENDMETHOD.ENDCLASS.Test Double manual
" Test DoubleCLASS ltcl_order_repo_double DEFINITION FOR TESTING. PUBLIC SECTION. INTERFACES: zif_order_repository. DATA: ms_return_order TYPE zorder, mt_saved_orders TYPE TABLE OF zorder.ENDCLASS.
CLASS ltcl_order_repo_double IMPLEMENTATION. METHOD zif_order_repository~get_order. rs_order = ms_return_order. ENDMETHOD.
METHOD zif_order_repository~save_order. APPEND is_order TO mt_saved_orders. ENDMETHOD.ENDCLASS.
" Test usando el doubleCLASS ltcl_order_service DEFINITION FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION. DATA: mo_cut TYPE REF TO zcl_order_service, mo_repo_mock TYPE REF TO ltcl_order_repo_double.
METHODS: setup. METHODS: test_process_order FOR TESTING.ENDCLASS.
CLASS ltcl_order_service IMPLEMENTATION. METHOD setup. mo_repo_mock = NEW #( ). mo_cut = NEW #( io_repository = mo_repo_mock ). ENDMETHOD.
METHOD test_process_order. " Given - Configurar mock mo_repo_mock->ms_return_order = VALUE #( order_id = '12345' status = 'NUEVO' amount = '100.00' ).
" When mo_cut->process_order( '12345' ).
" Then - Verificar que se guardó cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( mo_repo_mock->mt_saved_orders ) msg = 'Se debería haber guardado un pedido' ).
cl_abap_unit_assert=>assert_equals( exp = 'PROCESADO' act = mo_repo_mock->mt_saved_orders[ 1 ]-status ). ENDMETHOD.ENDCLASS.SQL Test Double Framework
CLASS ltcl_with_sql_double DEFINITION 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: test_get_active_orders FOR TESTING.ENDCLASS.
CLASS ltcl_with_sql_double IMPLEMENTATION. METHOD class_setup. " Crear entorno de test para la CDS View mo_environment = cl_cds_test_environment=>create( i_for_entity = 'ZI_ORDER' ). ENDMETHOD.
METHOD class_teardown. mo_environment->destroy( ). ENDMETHOD.
METHOD setup. " Limpiar datos de test antes de cada test mo_environment->clear_doubles( ). ENDMETHOD.
METHOD test_get_active_orders. " Given - Insertar datos de test DATA(lt_test_data) = VALUE ztab_order( ( order_id = '001' status = 'ACTIVO' amount = '100.00' ) ( order_id = '002' status = 'CERRADO' amount = '200.00' ) ( order_id = '003' status = 'ACTIVO' amount = '150.00' ) ).
mo_environment->insert_test_data( lt_test_data ).
" When SELECT * FROM zi_order WHERE status = 'ACTIVO' INTO TABLE @DATA(lt_result).
" Then cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) msg = 'Deberían haber 2 pedidos activos' ). ENDMETHOD.ENDCLASS.Testing de RAP Business Objects
Test de Behavior Implementation
CLASS ltcl_rap_behavior DEFINITION FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION. CLASS-DATA: mo_environment TYPE REF TO if_cds_test_environment, mo_bo_test TYPE REF TO if_rap_bo_test_run.
CLASS-METHODS: class_setup. CLASS-METHODS: class_teardown.
METHODS: setup. METHODS: test_create_order FOR TESTING. METHODS: test_validation_fails FOR TESTING.ENDCLASS.
CLASS ltcl_rap_behavior IMPLEMENTATION. METHOD class_setup. " Crear entorno para las entidades RAP mo_environment = cl_cds_test_environment=>create_for_multiple_cds( i_for_entities = VALUE #( ( i_for_entity = 'ZI_ORDER' ) ( i_for_entity = 'ZI_ORDERITEM' ) ) ).
" Crear test runner para RAP BO mo_bo_test = cl_rap_bo_test_run=>create( 'ZI_ORDER' ). ENDMETHOD.
METHOD class_teardown. mo_environment->destroy( ). ENDMETHOD.
METHOD setup. mo_environment->clear_doubles( ). mo_bo_test->clear( ). ENDMETHOD.
METHOD test_create_order. " Given/When - Ejecutar CREATE MODIFY ENTITIES OF zi_order ENTITY Order CREATE FIELDS ( customer_id description ) WITH VALUE #( ( %cid = 'CID_1' customer_id = '1000' description = 'Test Order' ) ) MAPPED DATA(ls_mapped) FAILED DATA(ls_failed) REPORTED DATA(ls_reported).
" Then cl_abap_unit_assert=>assert_initial( act = ls_failed-order msg = 'Create debería ser exitoso' ).
cl_abap_unit_assert=>assert_not_initial( act = ls_mapped-order msg = 'Mapped debería tener entrada' ). ENDMETHOD.
METHOD test_validation_fails. " Given - Datos inválidos " When MODIFY ENTITIES OF zi_order ENTITY Order CREATE FIELDS ( customer_id description ) WITH VALUE #( ( %cid = 'CID_1' customer_id = '' " Vacío - debería fallar validación description = 'Test' ) ) MAPPED DATA(ls_mapped) FAILED DATA(ls_failed) REPORTED DATA(ls_reported).
" Then cl_abap_unit_assert=>assert_not_initial( act = ls_failed-order msg = 'Validación debería fallar' ). ENDMETHOD.ENDCLASS.Assertions útiles
" Igualdadcl_abap_unit_assert=>assert_equals( exp = expected_value act = actual_value msg = 'Los valores deberían ser iguales').
" Tabla tiene n entradascl_abap_unit_assert=>assert_equals( exp = 5 act = lines( lt_result )).
" No inicialcl_abap_unit_assert=>assert_not_initial( act = lv_result msg = 'El resultado no debería ser inicial').
" Inicialcl_abap_unit_assert=>assert_initial( act = lt_errors msg = 'No debería haber errores').
" Bound (referencia)cl_abap_unit_assert=>assert_bound( act = lo_instance msg = 'La instancia debería estar bound').
" True/Falsecl_abap_unit_assert=>assert_true( act = lv_is_valid msg = 'Debería ser válido').
cl_abap_unit_assert=>assert_false( act = lv_has_errors msg = 'No debería tener errores').
" Forzar fallocl_abap_unit_assert=>fail( 'Este código no debería ejecutarse' ).
" Contiene textocl_abap_unit_assert=>assert_char_cp( exp = '*error*' act = lv_message msg = 'El mensaje debería contener error').
" Tabla contiene líneacl_abap_unit_assert=>assert_table_contains( table = lt_result line = ls_expected_line).Configuración de test
Niveles de riesgo
" HARMLESS - Sin efectos secundariosCLASS ltcl_test DEFINITION FOR TESTING RISK LEVEL HARMLESS.
" DANGEROUS - Puede modificar datosCLASS ltcl_test DEFINITION FOR TESTING RISK LEVEL DANGEROUS.
" CRITICAL - Puede afectar al sistemaCLASS ltcl_test DEFINITION FOR TESTING RISK LEVEL CRITICAL.Duración
" SHORT - Máx. 60 segundosCLASS ltcl_test DEFINITION FOR TESTING DURATION SHORT.
" MEDIUM - Máx. 300 segundosCLASS ltcl_test DEFINITION FOR TESTING DURATION MEDIUM.
" LONG - Sin límiteCLASS ltcl_test DEFINITION FOR TESTING DURATION LONG.Notas importantes / Mejores prácticas
- Usa inyección de dependencias para facilitar el testing.
- Crea Test Doubles para aislar la unidad bajo test.
- El SQL Test Double Framework permite testear sin base de datos real.
- Para RAP, usa
cl_rap_bo_test_runpara tests de comportamiento. - Sigue el patrón Given-When-Then para estructurar tests.
- Usa RISK LEVEL HARMLESS para tests que no modifican datos.
- Ejecuta tests con DURATION SHORT para feedback rápido.
- Integra tests en el ABAP Test Cockpit.