ABAP Unit es el framework de testing integrado en ABAP. Permite crear y ejecutar tests unitarios para garantizar la calidad del código.
Estructura básica
" Clase productivaCLASS zcl_calculator DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. METHODS: add IMPORTING iv_a TYPE i iv_b TYPE i RETURNING VALUE(rv_sum) TYPE i.
METHODS: divide IMPORTING iv_dividend TYPE i iv_divisor TYPE i RETURNING VALUE(rv_result) TYPE decfloat34 RAISING zcx_division_error.ENDCLASS.
CLASS zcl_calculator IMPLEMENTATION. METHOD add. rv_sum = iv_a + iv_b. ENDMETHOD.
METHOD divide. IF iv_divisor = 0. RAISE EXCEPTION TYPE zcx_division_error. ENDIF. rv_result = iv_dividend / iv_divisor. ENDMETHOD.ENDCLASS.
" Clase de test (local en el mismo include)CLASS ltcl_calculator DEFINITION FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION. DATA: mo_cut TYPE REF TO zcl_calculator.
METHODS: setup. METHODS: test_add_positive FOR TESTING. METHODS: test_add_negative FOR TESTING. METHODS: test_divide_normal FOR TESTING. METHODS: test_divide_by_zero FOR TESTING.ENDCLASS.
CLASS ltcl_calculator IMPLEMENTATION. METHOD setup. mo_cut = NEW #( ). ENDMETHOD.
METHOD test_add_positive. cl_abap_unit_assert=>assert_equals( exp = 5 act = mo_cut->add( iv_a = 2 iv_b = 3 ) ). ENDMETHOD.
METHOD test_add_negative. cl_abap_unit_assert=>assert_equals( exp = -1 act = mo_cut->add( iv_a = 2 iv_b = -3 ) ). ENDMETHOD.
METHOD test_divide_normal. cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '2.5' ) act = mo_cut->divide( iv_dividend = 5 iv_divisor = 2 ) ). ENDMETHOD.
METHOD test_divide_by_zero. TRY. mo_cut->divide( iv_dividend = 5 iv_divisor = 0 ). cl_abap_unit_assert=>fail( 'Se esperaba excepción' ). CATCH zcx_division_error. " Éxito - se lanzó la excepción esperada ENDTRY. ENDMETHOD.ENDCLASS.Métodos de fixture
CLASS ltcl_test DEFINITION FOR TESTING. PRIVATE SECTION. CLASS-DATA: gv_class_data TYPE string. DATA: mv_instance_data TYPE string.
" Se ejecuta una vez antes de todos los tests CLASS-METHODS: class_setup.
" Se ejecuta una vez después de todos los tests CLASS-METHODS: class_teardown.
" Se ejecuta antes de cada test METHODS: setup.
" Se ejecuta después de cada test METHODS: teardown.
METHODS: test_something FOR TESTING.ENDCLASS.
CLASS ltcl_test IMPLEMENTATION. METHOD class_setup. " Inicialización costosa que se comparte gv_class_data = 'Datos compartidos'. ENDMETHOD.
METHOD class_teardown. " Limpieza final CLEAR gv_class_data. ENDMETHOD.
METHOD setup. " Preparación para cada test mv_instance_data = 'Datos de test'. ENDMETHOD.
METHOD teardown. " Limpieza después de cada test CLEAR mv_instance_data. ENDMETHOD.
METHOD test_something. cl_abap_unit_assert=>assert_not_initial( mv_instance_data ). ENDMETHOD.ENDCLASS.Assertions
Igualdad
" Valores igualescl_abap_unit_assert=>assert_equals( exp = 100 act = lv_actual msg = 'Los valores deberían ser iguales').
" Con tolerancia para decimalescl_abap_unit_assert=>assert_equals( exp = '3.14159' act = lv_pi tol = '0.001').Valores iniciales
" Debería ser inicialcl_abap_unit_assert=>assert_initial( act = lt_errors msg = 'No debería haber errores').
" No debería ser inicialcl_abap_unit_assert=>assert_not_initial( act = lv_result msg = 'El resultado no debería estar vacío').Booleanos
" Truecl_abap_unit_assert=>assert_true( act = lv_is_valid msg = 'Debería ser válido').
" Falsecl_abap_unit_assert=>assert_false( act = lv_has_errors msg = 'No debería tener errores').Referencias
" Boundcl_abap_unit_assert=>assert_bound( act = lo_instance msg = 'La instancia debería existir').
" Not boundcl_abap_unit_assert=>assert_not_bound( act = lo_optional msg = 'La instancia no debería existir').Tablas
" Número de líneascl_abap_unit_assert=>assert_equals( exp = 5 act = lines( lt_result )).
" Tabla contiene líneacl_abap_unit_assert=>assert_table_contains( table = lt_result line = VALUE #( id = 1 name = 'Test' )).
" Tablas igualescl_abap_unit_assert=>assert_equals( exp = lt_expected act = lt_actual).Texto y patrones
" Contiene patrón (wildcards)cl_abap_unit_assert=>assert_char_cp( exp = '*error*' act = lv_message).
" Diferencia de cadenascl_abap_unit_assert=>assert_differs( exp = 'valor1' act = lv_actual).Forzar fallo
" Fallo explícitocl_abap_unit_assert=>fail( 'Este código no debería ejecutarse' ).
" Skip test (no es fallo)cl_abap_unit_assert=>skip( 'Test pendiente de implementación' ).Test de excepciones
METHOD test_exception_thrown. TRY. mo_cut->method_that_should_throw( invalid_input ). cl_abap_unit_assert=>fail( 'Se esperaba una excepción' ). CATCH zcx_my_exception INTO DATA(lx_error). " Verificar mensaje de excepción cl_abap_unit_assert=>assert_char_cp( exp = '*inválido*' act = lx_error->get_text( ) ). ENDTRY.ENDMETHOD.
METHOD test_no_exception. TRY. DATA(lv_result) = mo_cut->method_that_should_work( valid_input ). " Si llegamos aquí, no hubo excepción - OK CATCH zcx_my_exception INTO DATA(lx_error). cl_abap_unit_assert=>fail( |Excepción inesperada: { lx_error->get_text( ) }| ). ENDTRY.ENDMETHOD.Organización de tests
Tests en include separado
" En la clase principal (ZCL_MY_CLASS)" Include para tests locales: ZCL_MY_CLASS=CCAU
" Contenido del include*"* use this source file for your ABAP unit test classes
CLASS ltcl_my_class DEFINITION FOR TESTING. " ...ENDCLASS.Múltiples clases de test
" Tests unitariosCLASS ltcl_unit_tests DEFINITION FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. " ...ENDCLASS.
" Tests de integraciónCLASS ltcl_integration_tests DEFINITION FOR TESTING DURATION MEDIUM RISK LEVEL DANGEROUS. " ...ENDCLASS.Ejecutar tests
En ADT (Eclipse)
- Clic derecho en clase o paquete
- Run As -> ABAP Unit Test
- Ver resultados en la vista ABAP Unit
Con cobertura
- Clic derecho en clase o paquete
- Run As -> ABAP Unit Test With Coverage
- Ver cobertura en la vista ABAP Coverage
Filtrar por duración
Run Configuration:- Duration: SHORT (solo tests rápidos)- Duration: MEDIUM (incluye medios)- Duration: LONG (todos)Métricas de cobertura
| Métrica | Descripción |
|---|---|
| Statement Coverage | % de sentencias ejecutadas |
| Branch Coverage | % de ramas IF/CASE ejecutadas |
| Procedure Coverage | % de métodos llamados |
Notas importantes / Mejores prácticas
- Nombra los tests descriptivamente:
test_add_with_negative_numbers. - Sigue el patrón AAA: Arrange, Act, Assert (o Given-When-Then).
- Un test = una assertion principal (múltiples assertions relacionadas OK).
- Usa DURATION SHORT para tests que deben ser rápidos.
- RISK LEVEL HARMLESS para tests sin efectos secundarios.
- Crea fixtures reutilizables en
setup()yclass_setup(). - Aísla tests con Test Doubles (ver Test Doubles).
- Apunta a > 80% de cobertura de código.
- Integra tests en CI/CD.