Have an amazing solution built in RAD Studio? Let us know. Looking for discounts? Visit our Special Offers page!
Noticias

Test Unitarios; Framework DUnit (Entrega 2)

the future of starts demands massive productivity

Author: Germán Estévez

 Esta es la segunda entrada de la serie dedicada a pruebas unitarias. Si en la primera vimos una introducción a la programación guiada por pruebas, en esta segunda vamos a empezar a revisar los frameworks disponibles que nos ayudan a realizar los test unitarios. Concretamente en esta nos centraremos en DUnit.

Para realizar Test unitarios con Delphi disponemos de dos frameworks; Según la versión de Delphi podemos utilizarlos indistintamente, aunque a partir de la versión XE8 de Delphi, se recomienda utilizar DUnitX.

CREACIÓN DE PRUEBAS UNITARIAS (FRAMEWORKS)

Para realizar test unitarios con Delphi podemos utilizar los frameworks (ambos Open Source) DUnit y DUnitX.

A partir de la versión Delphi XE8 Embarcadero recomienda utilizar DUnitX, ya que DUnit ha quedado desactualizado. Si se ha trabajado con DUnit es relativamente fácil migrar los test ya existentes a DUnitX.

Lo que vamos a hacer es realizar los test con ambos paquetes y de esta forma también podremos ver las diferencias entre ambos y las ventajas prácticas que nos pueden aportar.

FRAMEWORK DUNIT

Para crear nuestro proyecto de pruebas unitarias que testee la clase TAritmeticaBasica de nuestra unit UClass.TAritmeticaBasica, podemos utilizar el asistente que ya trae el paquete. Si en nuestra versión de delphi no está instalado este paquete (DUnit), hay que descargarlo (de la dirección que hay más arriba) e instalarlo.

  1. Lo primero es crear un proyecto que utilice nuestra Unit. Sería nuestra aplicación que va a utilizar esta unit. Para ello crearemos un nuevo proyecto llamado PAritmeticaBasica y le añadiremos la unit anterior.
  2. A continuación vamos a generar, utilizando el asistente, el proyecto para las pruebas unitarias (y lo añadiremos al mismo grupo de proyectos). De esta forma tendremos nuestro proyecto y junto a el el proyecto de pruebas.

 

Para ello, desde el menú de File/New/Other/Unit Test seleccionamos Test Project. 1.- Arranca el asistente y lo configuramos de la siguiente forma:

  • Es el proyecto que usa nuestra Unit
  • Es el proyecto nuevo de testing que se va a generar.

2.- El segundo paso del asistente nos pregunta el framework de test a utilizar y el tipo de ejecución. Para este caso seleccionaremos lo siguiente:

3.- Una vez finalizamos el asistente, ya disponemos en nuestro grupo de los 2 proyectos que hemos creado. El de nuestra aplicación y el de los test unitarios asociados. No hemos acabado aquí, pues el Framework nos ofrece la posibilidad de crear además la estructura de los test que necesitamos.

4.- Seleccionando el proyecto de test “PAritmeticaBasicaDUnitTest“, desde el menú: File/New/Other/Unit Test seleccionamos Test Case. Arrana el segundo Wizard, en este caso no para el proyecto, sino para los test.

5.- Seleccionamos la Unit sobre la que queremos crear los test y automáticamente el asistente nos mostrará los métodos disponibles para que podamos seleccionar cuales queremos probar ( a cuales queremos generarle pruebas unitarias).

6.- Vemos que en esa lista ya aparecen los métodos de nuestra clase TAritmeticaBasica. Seleccionamos todos y pulsamos siguiente.

7.- El último paso define el nombre para la unit y el proyecto al que la vamos a añadir.

8.- Una vez finalizado el asistente, guardamos los cambios y ya disponemos de los 2 proyectos en un grupo. El primero que usa nuestra unit y el segundo que testea dicha unit. Por ahora todo está en el “esqueleto” y sin implementación, pero ya podemos compilar y ejecutar nuestro proyecto de test (ver NOTA1).

NOTA1: Para compilar y ejecutar debemos modificar el procedimiento TestFactorizacion, de la unit Test.UClass.TAritmeticaBasica.pas y dejarlo como el que hay a continuación.

Comentamos la línea porque el elemento en nuestro proyecto de ejemplo llega sin asignar y completamos la definición (que el asistente no acaba de completarla bien).

Si ejecutamos nuestro test, ya podemos ver los resultados.

Como podemos ver los test (con lo que actualmente tenemos) dan todos como correctos. Es normal, porque ni tenemos código en nuestra función, ni tenemos validaciones correctas en nuestros test.

Si seguimos los pasos que definimos en la primera entrega de esta serie para la definición y creación de test unitarios, lo siguiente que debemos hacer es dotar de lógica a nuestros test. Cada uno podemos crear los nuestros, en mi caso, por ejemplo, empezando por el test para números primos; Para esta comprobación he generado la siguiente casuística:

La implementación de estos test es la que se ve a continuación. Ahora debo definir el resultado correcto que yo espero en cada test, si la función TestEsNumeroPrimo funcionase correctamente. Para ello voy a utilizar las 2 funciones que me provee el framework de testing:

  • CheckTrue
  • CheckFalse

Como ya podéis imaginar por el nombre estas funciones, se usan para comprobar si un resultado de una función devuelve el resultado booleano esperado.

Recordemos que nuestra función para calcular si un número es primo (EsNumeroPrimo) todavía no está implementada y devuelve siempre True, así que es previsible que la ejecución de los test devuelve errores.

La implementación de los test es la siguiente (*NOTA1*):

Para cada uno de los números utilizamos CheckTrue o CheckFalse dependiendo del resultado que deberíamos obtener. Si ahora volvemos a ejecutar los test obtendremos el siguiente resultado. Ejecucion_Test_NumeroPrimo   NOTA: A priori a uno se le podría ocurrir programar los test para la función que comprueba si un número es primo de una forma similar a esta (*NOTA2*):

tdd_cycle

El problema es que de esta forma incumpliríamos las premisas de un test unitario que comentamos en la primera entrada de esta serie; Que un test “no debe tener dependencias”; De esta forma, cuando el primer test falle, ya no se comprobará ninguno más. Por lo tanto la ejecución de uno de estos test, depende de que los anteriores hayan funcionado correctamente.

Para corregir esto en DUnit, la única forma es parametrizar los test unitarios como veremos en el último punto de esta entrada.

Por último, y siguiendo el esquema que vemos a la derecha, ahora que ya hemos implementado los test, debemos implementar la función que estamos probando (EsNumeroPrimo).

Vosotros mismos podéis intentar implementarla y probar. En mi caso he utilizado/adaptado esta que me ha parecido simple y que he encontrado aquí.

Ahora que ya tenemos nuestra función, podemos volver a ejecutar los test para comprobar la validez de nuestra implementación.

El resultado que obtenemos es el siguiente.

Resultado_Test_Unitarios

Como era de esperar, si nuestra función es correcta, los resultados del Test lo confirman.

Una vez que vemos las dos opciones anteriores nos damos cuenta que la primera opción (*NOTA1*) sería la correcta sintácticamente, pero poco práctica y la segunda (*NOTA2*) aunque parezca más óptima, no es correcta sintácticamente según TDD, dado que los Test deben ser independientes.

Una última opción sería “parametrizar” los test. En el caso de DUnit esta parametrización debemos hacerla nosotros, como ya veremos otros frameworks facilitan este trabajo.

CLASE PARA PAMETRIZAR TEST CON DUNIT

La idea en esta caso sería crear una clase que nos permita parametrizar la creación de test, de forma que podamos usar parámetros para crearlos y evitar así tener que hacerlo de forma manual.

Esta opción se ajusta más a las directrices de TDD y además nos evitará trabajo si el número de casos a probar en un test es grande, a cambio de incrementar un poco la complejidad de creación de los test.

Nuestra clase de parametrización de números primos tendrá la siguiente estructura:

Y la de parametrizar test de factorización una muy similar:

Podemos revisar la documentación de DUnit en Sourceforge y encontraremos la funcionalidad de los métodos que hemos definido:

  • SetUp: Se ejecuta antes de llamar al método que ejecuta el test (Run). De forma que lo utilizaremos para Inicializaciones.
  • TearDown: Se ejecuta después del método que ejecuta el test (Run) y normalmente lo utilizaremos para liberar recursos que hayamos podido crear en el método SetUp.
  • GetName: Nos permite configurar utilizando parámetros el nombre del test que posteriormente aparecerá en la ventana de resultados.
  • Create: Nos permite crear realmente cada uno de los test que vamos a parametrizar. Utilizando el constructor debemos pasar todos los datos necesarios para parametrizar nuestro test.
  • Run: Es el método que realmente ejecuta nuestro test.
    Una vez que ya tenemos la definición vemos la implementación de la clase:
    Por último falta definir el procedimiento que registra todos los test que necesitemos para esta clase utilizando parámetros.
    Si utilizando esta nueva clase, ejecutamos los test, veremos el resultado tal y como esperamos.

Resultado Hasta aquí esta entrada en la que hemos visto cómo crear test unitarios con el Framework DUnit. En la siguiente haremos algo similar con DUnitX y veremos algunas diferencias entre ellos. Os dejo el código fuente completo de los proyectos. Como siempre, cualquier comentario, sugerencia, aportación,… será bienvenida. Hasta la próxima.  


Reduce development time and get to market faster with RAD Studio, Delphi, or C++Builder.
Design. Code. Compile. Deploy.
Start Free Trial   Upgrade Today

   Free Delphi Community Edition   Free C++Builder Community Edition

Leave a Reply

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

IN THE ARTICLES