Pruebas unitarias en CodeIgniter

Las pruebas unitarias (unit testing) sirven para verificar el correcto funcionamiento de un módulo o de una librería. Consisten en realizar una serie de chequeos para ver de qué manera responde el código creado. Estas pruebas deben acompañar al proyecto que se está desarrollando y ser ejecutadas de forma periódica a medida que se va avanzando. De esta forma, al ir agregando y modificando el código, se puede tener la certeza de que no se está alterando la funcionalidad básica de los módulos creados.

En sí, las pruebas unitarias son código de programación que se encarga de probar a otro código de programación. Presentan muchas ventajas: hacen que la refactorización sea más simple debido a que es más fácil ubicar los errores, hace que la posterior integración de los módulos sea mucho más sencilla y quienes han trabajado en equipo sabrán que son una necesidad para evitar que el código se esté rompiendo a cada rato.

Pruebas unitarias en PHP

En mi caso, aprendí programación orientada a objetos con Java y siempre he intentado llevar los objetos a PHP. Con las nuevas versiones del lenguaje de servidor, fui haciendo algunos avances y llegué a probar las librerías PHPUnit, de PEAR, y SimpleTest.

Al empezar a explorar CodeIgniter, hace ya tiempo, una de las primeras cosas que busqué fue una librería similar a JUnit, que era la encargada de las pruebas unitarias cuando trabajaba en Java. Hasta se integraba en Eclipse, siendo muy cómodo su uso. Pero no encontré nada y desarrollé un tiempo sin pruebas unitarias, lo que me molestaba mucho.

Tiempo después, volví a buscar una librería para pruebas unitarias que funcione con CodeIgniter. Aparecieron dos: CIUnit y TOAST. La segunda, muy similar a JUnit. Lamentablemente, no me funcionó ninguna.

Sin embargo, en alguna versión nueva de CodeIgniter (no me fijé bien en cual) apareció una librería de pruebas unitarias y aunque a simple vista me decepcionó mucho, finalmente llegué a utilizarla y a acostumbrarme.

Pruebas unitarias en CodeIgniter con Unit Test

Primero aclaremos un aspecto importante de cómo va a funcionar esto. La prueba unitaria deberá llevarse a cabo y luego mostrarse su resultado. La forma que tiene este framework es, por supuesto, web. No podemos esperar que se abra una ventana, pero sí que se cargue una página y muestre el resultado en HTML. Por lo tanto, necesitaremos un controlador que realice las pruebas.

Para este ejemplo, voy a probar una librería en la que estuve trabajando y que se encarga de sumar y restar horas.

Empecemos definiendo el controlador. Hay que ubicarlo junto con los otros controladores, preferiblemente en un directorio de nombre test. También es recomendable tener un archivo controlador por cada clase o módulo a probar.

Es un controlador normal que hereda de CI_Controller. En el constructor se carga la librería de pruebas unitarias, que se llama unit y luego las librerías o los modelos que se quieren probar. En mi caso, la librería Hora.

Luego de cargar la librería de pruebas unitarias, es accesible por medio de $this->unit

Ahora veamos como es un método de prueba.

Por costumbre y por analogía a otros lenguajes, el nombre del método lo empecé con test, aunque no es obligatorio. Dentro de este método se llevará a cabo la prueba.

Como lo que estamos probando es la librería Hora, estamos suponiendo que las funciones a probar están terminadas o por lo menos completadas a un determinado nivel como para poder probarlas. No es obligatorio probar una única función por método, pero sí recomendable para mantener la organización del código.

De esta forma, nuestro testRestarHora se va a encargar únicamente de probar la función restarTiempo de la librería Hora. Como ven, hay tres bloques de código, cada uno es una subprueba, o un chequeo particular. En conjunto, los tres se encargan de verificar si la función responde de manera correcta.

No es cuestión de hacer las pruebas por que sí. Hay que pensarlas cuidadosamente. La idea es poner a prueba el módulo que hemos programado para verificar si hace lo que tiene que hacer. Es importante comprobar la respuesta que tiene en situaciones especiales.

Siguiendo este ejemplo, el de una librería que se encarga de sumar y restar horas, habría que realizar pruebas básicas para ver si la suma y resta de horas es correcta y luego cabría hacerse las siguiente preguntas: ¿Qué pasa si se suma más de 24 horas?, ¿Qué pasa si se resta un número de horas mayor a uno menor?  Cada una de estas preguntas puede dar lugar a una prueba distinta.

Analicemos un bloque de código, los otros son exactamente iguales.

La función que realizará el test es $this->unit->run y necesita los siguientes parámetros: el primero es el valor que devuelve la función que se quiere probar. El segundo, es el valor esperado, es decir, lo que debería dar. El tercero es el nombre del test, que es arbitrario y lo elegimos nosotros.

Ahora, ¿cómo hacer el test? Tenemos que pensar en una operación de entrada y salida respecto del módulo a probar. Con una determinada entrada de datos, debería producir una salida esperada. En este caso, si restamos 45 minutos menos 20 minutos, el resultado debería ser 25 minutos. La librería Hora maneja el formato HH:MM como cadena de caracteres, así que los datos de entrada y el de salida van escritos de esa manera.

Se usa el método que se desea probar, restarTiempo, pasándole los parámetros de entrada. Se obtiene la salida en $suma y finalmente se utiliza el método run de unit para correr el test. Lo que hace run es simplemente comparar el primer parámetro con el segundo. Si son iguales, es decir, si el resultado es el esperado, entonces la prueba ha sido superada. Si no, la prueba falla.

Por defecto, si se pasan esos dos parámetros, unit va a chequear igualdad. Es el equivalente al assertEquals, para quienes tengan experiencia trabajando con otros lenguajes y con librerías de pruebas unitarias.

También se puede comparar tipo de dato, si se pasa un segundo parámetro como en el siguiente ejemplo:

Cada uno, es una prueba de tipo. Nuevamente, para quienes ya hayan trabajado con pruebas en otros lenguajes, equivale a los distintos asserts.

Mostrar los resultados de las pruebas unitarias

Todo muy lindo, pero hasta aquí, no hemos visto nada. Para ver cómo salieron las pruebas, debemos acceder al controlador que las contiene. En este caso index.php/test/testHora/testRestarHora. Es decir, la URL base del proyecto. Luego, el directorio donde estén los tests, a continuación el nombre del controlador y finalmente, el nombre del método que realiza la prueba.

Si prestan atención, verán que no se está cargando una vista. Pero sí se está poniendo un echo delante de la ejecución del método run.

Eso muestra por pantalla el resultado del test.

test-visualizacion

Se ven los datos de cada uno de los test y su resultado, que puede ser correcto o incorrecto. En esta primera captura, como se puede ver, son todos correctos. Si alteramos el resultado esperado del primer test, tendremos la siguiente salida.

test-fallido

Como era de esperar, el primer test ahora es incorrecto.

Esta no es la única manera de mostrar las pruebas por pantalla. También se puede hacer con la siguiente instrucción:

Si no les gusta la plantilla que usa CodeIgniter para mostrar los resultados, entonces pueden cambiarla. El manual oficial presenta un método de unit que se llama set_template y al recibir HTML, con algunas palabras clave que aparecen entre llaves, permite personalizar la plantilla.

 

Conclusiones

Las pruebas unitarias representan una gran ventaja para los desarrolladores. Su implementación en PHP es posible y también en CodeIgniter, que trae una librería propia para realizar este tipo de chequeos.

Su uso es bastante simple. Se necesita crear un controlador para probar un módulo determinado y luego cargar la librería unit. La ejecución del test se lleva a cabo gracias al método run, que realiza la comparación entre el valor que devuelve el método a probar y el valor esperado. Se pueden crear tantos controladores como clases se deban probar y también en cada uno de ellos se pueden crear tantos métodos de prueba como sea necesario, aunque generalmente cada método probará una única función.

No se carga una vista para visualizar la salida, simplemente se utiliza echo y al acceder a la ubicación que corresponde, se puede ver el resultado que arroja el test.

Hay algunos aspectos más a tener en cuenta acerca de las pruebas unitarias, pero los pueden encontrar en el manual oficial de CodeIgniter.

Soy programador web y me desempeño como Líder Técnico y de Proyectos en Polar Bear Development. Trabajo con tecnologías como PHP, Javascript, MySQL y HTML5 para el desarrollo de sitios y sistemas web. Me especializo en Zend Framework 2 y otros frameworks MVC, como también en WordPress y otros CMS. Lidero equipos de desarrolladores trabajando con Scrum. Vivo en Buenos Aires, Argentina.