En el artículo anterior os hablaba de las ventajas de integrar el ABAP Unit Testing en un proceso de desarrollo. En este artículo y los siguientes nos vamos a meter en harina y empezar a ver todo desde el punto de vista técnico.
Cómo se crea el entorno de pruebas?
Para crear un entorno de pruebas unitario es necesario crear al menos una clase local de pruebas (se pueden crear varias, ya que podemos necesitar varios entornos de pruebas diferentes). Si el elemento a testear es una clase, ésta ya dispone de un lugar específico al que se puede acceder desde el menú de opciones superior.
Para cualquier otro elemento a testear, la clase se debe definir/implementar en los Includes correspondientes a las definiciones/implementaciones locales.
La clase local de pruebas es como cualquier otra clase habitual. Sólo presenta las siguientes particularidades en su definición:
CLASS ltc_nombre_de_la_clase DEFINITION FOR TESTING RISK LEVEL x DURATION y.
- El nombre de la clase, al que es aconsejable añadirle el sufijo LTC_ (local test class).
- FOR TESTING, que evita que la clase sea ejecutada por accidente en un entorno productivo. Esta clase es segura y sólo se puede ejecutar en modo de prueba.
- RISK LEVEL, que declara el nivel de riesgo de la prueba. Hay tres valores posibles: HARMLESS (no tiene efecto sobre datos persistentes ni sobre ajustes del sistema), DANGEROUS (puede modificar datos persistentes) y CRITICAL (puede modificar ajustes del sistema y Customizing).
- DURATION, establece la duración de los tests. También hay tres posibles valores: SHORT (menos de un minuto), MEDIUM (entre uno y cinco minutos) y LONG (más de cinco minutos).
Si cualquiera de nuestras pruebas se sale del entorno que hemos definido (en cuanto a duración o en cuanto a alteración de datos persistentes, etc.), su ejecución se detendrá (dependiendo de la modalidad de ejecución) y obtendremos el correspondiente mensaje de error en la ventana de resultados.
Una vez definida la clase, ya es detectada por la herramienta ABAP Unit Browser . Con esta herramienta podemos ejecutar y analizar los tests que definamos en la/s clase/s de pruebas. Para activar la herramienta, hay que ir a la transacción SE80->Utilidades->Opciones->Pestaña Workbench (general) y activar la casilla Browser de test unidad ABAP.
El browser detectará todos los elementos que poseen clase de test y nos los mostrará filtrados por el criterio que escojamos (paquete, clase, función, autor, etc.). Desde el browser se podrán ejecutar las clases de test que queramos pulsando el botón derecho del ratón.
Creación de los casos de prueba
Cada una de las pruebas que queramos realizar, la debemos definir como método de la clase de pruebas y añadirle el prefijo FOR TESTING.
METHODS Nombre_Del_Método_de_Pruebas FOR TESTING.
Esto es debido a que dentro de la clase de pruebas podemos definir todos los métodos y miembros que necesitemos (sólo se admiten miembros y métodos PRIVADOS), aunque durante la ejecución de la clase de pruebas sólo se ejecutarán los métodos definidos FOR TESTING. El resto de métodos reciben el nombre de métodos de ayuda y tan sólo servirán para hacer nuestro código más legible y fácil de modificar.
Los métodos de prueba también presentan la particularidad de que no admiten parámetros de ningún tipo. Tienen que tener una estructura definida y ordenada en tres partes fundamentales que nunca deben faltar:
- Datos de partida, donde se realizan las operaciones de configuración de la prueba (por ejemplo, si estamos probando una función, se almacenan en variables los parámetros de entrada que queremos probar).
- Realización de la prueba, en donde el objeto de prueba es invocado (siguiendo el ejemplo, se hace una llamada a la función con los parámetros definidos en el paso anterior).
- Contraste de los resultados, en donde se comparan los resultados obtenidos por el objeto de pruebas con los resultados que esperamos obtener a priori. Esto se hace a través de los métodos de la clase CL_ABAP_UNIT_ASSERT, que tiene los métodos suficientes (EQUALS, IS_INITIAL, IS_BOUND, DIFFERS, etc.) para contrastar los resultados obtenidos (parámetro ACT de los métodos) con los esperados (parámetro EXP de dichos métodos). Si alguno de ellos difiere, el test se considera fallido y el sistema nos proporciona toda la información necesaria para saber cuál ha sido el problema.
Además del prefijo FOR TESTING, hay 4 métodos concretos que podemos definir si los necesitamos: CLASS_SETUP, SETUP, TEARDOWN y CLASS_TEARDOWN. Estos métodos los comentaremos a continuación.
Una vez que tenemos definida la clase de pruebas y algún método de pruebas, podemos iniciar la ejecución. La ejecución es cíclica y tiene las siguientes fases:
- Ejecución del método estático CLASS_SETUP. Se ejecuta una única vez por cada clase de prueba. Sirve fijar o establecer todos los datos que van a ser comunes a todas las pruebas que se van a realizar dentro de la clase de pruebas. De esta forma, eliminaremos duplicidades en código de asignación de parámetros comunes.
- Ejecución del método SETUP. Se ejecuta siempre antes de cada método de prueba. Sirve para establecer un entorno seguro de prueba. Por ejemplo, si estamos testeando un objeto de una clase determinada, en este método se debe llamar al constructor del objeto y crearlo de nuevo. De esta forma, eliminamos la posibilidad de obtener falsos positivos (o negativos) debido al almacenamiento de datos de pruebas anteriores en el objeto.
- Ejecución del método de prueba. En esta fase se ejecuta el método de prueba (FOR TESTING) propiamente dicho.
- Ejecución del método TEARDOWN. Se ejecuta siempre después de cada método de prueba. Sirve para relaizar las operaciones de limpieza de memoria y datos necesarios para eliminar todo posible rastro dejado por el método de prueba.
- Ejecución del método estático CLASS_TEARDOWN. Se ejecuta una única vez por cada clase de prueba tras el último método TEARDOWN. Sirve para realizar las operaciones de limpieza de memoria y datos necesarios para eliminar todo posible rastro dejado por la clase de prueba.
Los métodos de prueba siempre se van a ejecutar en un orden aleatorio, para que no haya posibilidad de enmascaramiento de fallos debido a un orden preestablecido.
Análisis de los resultados
Tras la ejecución de la/s clase/s de test, obtenemos los resultados. Los resultados están organizados en dos pestañas: ABAP Unit Results y Coverage Metrics.
La pestaña ABAP Unit Results nos informa de una manera sencilla, ordenada y muy visual del resultado de los diferentes tests. Luz verde indica resultado positivo y luz roja resultado negativo.
Cuando hay un error, el sistema nos da toda la información necesaria para que podamos aislarlo y corregirlo.
La pestaña Coverage Metrics nos da información sobre qué porcentaje de código se ha probado, qué procedimientos se han testeado e incluso qué líneas de código se han utilizado.
El código sobre fondo verde indica código utilizado en los tests, mientras que el fondo rojo nos indica código no utilizado.
De esta forma, podemos ver qué partes del código no se han testeado aún y diseñar más tests para cubrirlas.
Conclusión
Este artículo tan sólo pretende ser una breve introducción a un tema tan importante como apasionante (a nivel teórico y práctico) y profundo. Esperamos ir explicando cosas más concretas en futuras entregas, como ejemplos de tests, técnicas para convertir código existente en testeable, técnicas de desarrollo orientado al testeo, técnicas de inyección de suplantadores de dependencias, ejecución de Unit Tests multinivel, creación de objetos espía, automatización de la ejecución de tests….en fin, el tema da para mucho. Por ahora, esperamo que os haya servido para despertar vuestra curiosidad y mentalizaros de que realizar estos test “tempranos” es necesario y ahorra muchísimo tiempo en debugging. ¡Ahora, a convencer a vuestros responsables de desarrollo!