ABAP Unit Testing: Guía completa de tests unitarios

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

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 productiva
CLASS 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 iguales
cl_abap_unit_assert=>assert_equals(
exp = 100
act = lv_actual
msg = 'Los valores deberían ser iguales'
).
" Con tolerancia para decimales
cl_abap_unit_assert=>assert_equals(
exp = '3.14159'
act = lv_pi
tol = '0.001'
).

Valores iniciales

" Debería ser inicial
cl_abap_unit_assert=>assert_initial(
act = lt_errors
msg = 'No debería haber errores'
).
" No debería ser inicial
cl_abap_unit_assert=>assert_not_initial(
act = lv_result
msg = 'El resultado no debería estar vacío'
).

Booleanos

" True
cl_abap_unit_assert=>assert_true(
act = lv_is_valid
msg = 'Debería ser válido'
).
" False
cl_abap_unit_assert=>assert_false(
act = lv_has_errors
msg = 'No debería tener errores'
).

Referencias

" Bound
cl_abap_unit_assert=>assert_bound(
act = lo_instance
msg = 'La instancia debería existir'
).
" Not bound
cl_abap_unit_assert=>assert_not_bound(
act = lo_optional
msg = 'La instancia no debería existir'
).

Tablas

" Número de líneas
cl_abap_unit_assert=>assert_equals(
exp = 5
act = lines( lt_result )
).
" Tabla contiene línea
cl_abap_unit_assert=>assert_table_contains(
table = lt_result
line = VALUE #( id = 1 name = 'Test' )
).
" Tablas iguales
cl_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 cadenas
cl_abap_unit_assert=>assert_differs(
exp = 'valor1'
act = lv_actual
).

Forzar fallo

" Fallo explícito
cl_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 unitarios
CLASS ltcl_unit_tests DEFINITION FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
" ...
ENDCLASS.
" Tests de integración
CLASS ltcl_integration_tests DEFINITION FOR TESTING
DURATION MEDIUM
RISK LEVEL DANGEROUS.
" ...
ENDCLASS.

Ejecutar tests

En ADT (Eclipse)

  1. Clic derecho en clase o paquete
  2. Run As -> ABAP Unit Test
  3. Ver resultados en la vista ABAP Unit

Con cobertura

  1. Clic derecho en clase o paquete
  2. Run As -> ABAP Unit Test With Coverage
  3. 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étricaDescripció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() y class_setup().
  • Aísla tests con Test Doubles (ver Test Doubles).
  • Apunta a > 80% de cobertura de código.
  • Integra tests en CI/CD.