CON PHPUNIT ING. LEONARDO TORRES ARGOMEDO EJEMPLOS DE PRUEBAS UNITARIAS CON PHPUNIT Una de las tareas ms importantes involucradas en la gestin de este cdigo son las pruebas sobre el cdigo generado. Los dos tipos de pruebas ms empleados en la prctica son los test funcionales y las pruebas unitarias. En los test funcionales se evala la aplicacin como un todo y se comprueba que se alcanzan unos determinados resultados tras realizar una serie de acciones sobre la aplicacin. En las pruebas unitarias al contrario se analiza una porcin de cdigo que pueda ser analizada de manera aislada, como por ejemplo funciones y mtodos. A los que despus de pasarle unos parmetros de entrada debemos obtener otros parmetros de salida claramente definidos. Ambos tipos de pruebas pueden servir a muchos niveles en el ciclo evolutivo de la aplicacin: documentacin, pruebas de regresin, anlisis de bugs etc. Phpunit es un script php que se instala con PHPUnit. Su funcin es buscar y ejecutar las pruebas que tengamos definidas en el fichero que le pasamos como argumento. Finalmente devolver un informe sobre el estado de las pruebas y los errores que se ha ido encontrando. Lo aconsejable es tener preparadas incluso antes de empezar a desarrollar mtodos o funciones, pruebas sobre estos y a medida que se vaya probando el cdigo o solventando bugs en el cdigo (incluso antes de corregir el problema) ir generando pruebas adicionales sobre las distintas partes del cdigo. Antes de enviar nada a produccin deberemos ejecutar todas las pruebas. Esto te suena familiar: Haz venido desarrollando una aplicacin durante horas y sientes como si estuvieras yendo en crculos. Corriges un bug y otro aparece. Algunas veces, es el mismo error que encontraste hace 30 minutos, a veces es uno nuevo, pero esta relacionado con el primero. Para la mayora de desarrolladores, la depuracin implica hacer clic en todo el sitio o poner una gran cantidad de declaraciones de debugging esperando encontrar un problema. Las pruebas de unidad o pruebas unitarias de una aplicacin no slo nos ahorran muchos dolores de cabeza, sino que pueden proporcionarnos un cdigo mucho ms fcil de mantener, permitiendo que realicemos cambios ms grandes (como refactorizaciones importantes) sin tener dubitaciones. OBS: La refactorizacin (del ingls refactoring) es una tcnica de la ingeniera de software para reestructurar un cdigo fuente, alterando su estructura interna sin cambiar su comportamiento externo. La clave de las pruebas unitarias es definir lo que entendemos por unidad. Una unidad es simplemente un pedazo de funcionalidad que realiza una accin especfica (mdulo), de la cual podemos probar el resultado. Una prueba unitaria, entonces, es una prueba para asegurarnos de que un trozo de funcionalidad hace lo que debera hacer. Una vez que hemos escrito una serie de pruebas, cada vez que hagamos algn cambio en el cdigo, todo lo que tenemos que hacer es ejecutar el conjunto de pruebas y ver que todo siga igual. De esta manera, podemos asegurarnos de no perder alguna funcionalidad que ya habamos logrado. ALGUNAS RAZONES O PENSAMIENTOS QUE NOS LLEVAN ERRNEAMENTE A PENSAR QUE LAS PRUEBAS NO SON NECESARIAS Se tarda demasiado tiempo: Una de las mayores preocupaciones sobre la escritura de pruebas es que termina tomando demasiado tiempo el generarlas. Claro, algunos IDEs permiten autogenerar un conjunto de pruebas bsicas, pero sentarse y escribir una buena prueba completa del cdigo, lleva su tiempo. Al igual que muchas de las mejores prcticas en el desarrollo, una pequea inversin de tiempo para hacer las cosas de la manera correcta puede ahorrarnos mucho tiempo durante la vida til de un proyecto. No hay necesidad de probar: El cdigo ya funciona!: Otra afirmacin que se suele escuchar de los desarrolladores sobre el conjunto de pruebas es que la aplicacin ya funciona, por lo que no hay necesidad real para probarla. Ellos conocen su aplicacin y saben que hacer cuando hay que corregir un error en cuestin de segundos. Pero, coloquemos a un desarrollador nuevo en el lugar del anterior, para empezar a ver por qu las pruebas son una buena idea. No es divertido La ltima razn de por qu a los desarrolladores no les gustan las pruebas es que simplemente no se divierten escribindolas. Los desarrolladores, por naturaleza, quieren resolver problemas. Escribir cdigo es como formar algo de la nada, creando del caos algo til. Como resultado, ellos ven a las pruebas como algo aburrido, una tarea que puede hacerse uno de estos das, si se tiene el tiempo, despus de que el verdadero trabajo se haya hecho. EJEMPLOS DE USO: Una vez instalado, podemos empezar a crear casos de pruebas. Con PHPUnit, la cosa ms bsica que escribiremos es un caso de prueba. Un caso de prueba es slo un trmino para una clase con varias pruebas distintas, todas relacionadas con la misma funcionalidad. Hay algunas reglas que necesitamos tener presentes a la hora de escribir los casos, a fin de que trabajen con PHPUnit: Frecuentemente, querremos que nuestra clase de prueba extienda la clasePHPUnit_Framework_TestCase. Esto nos dar acceso a ciertas funcionalidades como los mtodos para las pruebas setUp() y tearDown(). El nombre de la clase de prueba debe imitar el nombre de la clase que se esta probando. Por ejemplo, para probar RemoteConnect, utilizaremos RemoteConnectTest. Al crear mtodos de prueba, es necesario que empiecen con la palabra test, como testDoesLikeWaffles(). Los mtodos deben ser pblicos. Podemos tener mtodos privados en las pruebas, pero no se podrn ejecutar como pruebas por PHPUnit. Los mtodos de prueba no recibirn ningn parmetro. Cuando escribamos las pruebas, es necesario que sean lo ms autnomas posibles, tratando de que ellas mismas se necesiten. Esto puede ser frustrante a veces, pero nos proporcionar unas pruebas ms limpias y eficaces. Tenemos que empezar con algunas funciones de prueba, as que aqu tenemos la clase con la que vamos a trabajar en los siguientes ejemplos. Esto es lo que va en la librera RemoteConnect.php: <?php class RemoteConnect { public function connectToServer($serverName=null) { if($serverName==null){ throw new Exception("Este no es un nombre de servidor!"); } $fp = fsockopen($serverName,80); return ($fp) ? true : false; } public function returnSampleObject() { return $this; } } ?> As, por ejemplo, si vamos a probar esta funcionalidad para hacer una peticin a un servidor remoto, el test podra tener este aspecto: <?php require_once('RemoteConnect.php'); class RemoteConnectTest extends PHPUnit_Framework_TestCase { public function setUp(){ } public function tearDown(){ } public function testConnectionIsValid() { // prueba para asegurarse de que el objeto de un fsockopen es vlido $connObj = new RemoteConnect(); $serverName = 'www.google.com'; $this->assertTrue($connObj->connectToServer($serverName) !== false); } } ?> Se podrn percatar de que la clase extiende la clase TestCase de PHPUnit, por lo que una gran cantidad de funcionalidades vienen con ella. Los dos primeros mtodos setUp ytearDown- son ejemplos del tipo de funcionalidad integrada. Son funciones de ayuda que se ejecutan como parte de la prueba normal de funcionamiento. Se ejecutan antes de las pruebas y despus de que todo se ha ejecutado, respectivamente. A pesar de su utilidad, no vamos a centrarnos en ellos an. El objetivo real es el mtodo testConnectionIsValid(). Este mtodo establece el entorno necesario con la creacin de una nueva instancia de nuestra clase RemoteConnect y llama al mtodo connectToServer(). Ahora, veamos el verdadero negocio de nuestra prueba. Ves que assertTrue est ah? Esa es una de las funciones de ayuda de PHPUnit, de los cuales hay unos cuantos. assertTrue es la ms simple asercin: lo nico que hace es comprobar que una expresin booleana es true. Otras funciones de ayuda pueden poner a prueba las propiedades del objeto, la existencia de archivos, la presencia de una clave dada en una matriz o una coincidencia en una expresin regular, slo por mencionar algunas. En este caso, queremos estar seguros de que el resultado de connectToServer no sea false, esto quiere decir que nuestra conexin ha fallado por alguna razn. PARA EJECUTAR LAS PRUEBAS Ejecutar las pruebas es tan sencillo como llamar al ejecutable phpunit y sealar las pruebas. Aqu tenemos un ejemplo de como llamar a nuestra prueba: phpunit /path/to/tests/RemoteConnectTest.php Sencillo, cierto? La salida es muy simple: Por cada una de las pruebas del caso de prueba, PHPUnit se ejecuta a travs de ellos y recoge algunas estadsticas como el nmero de pruebas y aserciones. He aqu una vista de la salida de nuestro ejemplo: PHPUnit 3.4 by Sebastian Bergmann . Time: 1 second Tests: 1, Assertions: 1, Failures 0 Para cada prueba que ejecutamos, veremos un punto (.) si se tiene xito (como arriba), una F si ocurri un error, y una I si la prueba esta marcada como incompleta o una S si se ha marcado como Omitida (Skipped). De forma predeterminada, PHPUnit est configurado para ejecutarse a travs de un conjunto de pruebas a la vez e informar las estadsticas totales en un informe sencillo. Hay muchas y diferentes aserciones que pueden ayudarnos a probar los resultados de todos los tipos de llamadas en nuestras aplicaciones. Algunas veces, podemos tener un poco ms de creatividad para probar piezas de funcionalidad ms complejas; pero las aserciones provistas por PHPUnit cubren la mayora de los casos que querremos probar. Aqu esta una lista de algunas de las cosas ms comunes que podemos encontrar al usarla en nuestras pruebas: TEST-DRIVEN DEVELOPMENT (DESARROLLO BASADO EN PRUEBAS) El desarrollo basado en pruebas. El Test Driven Development oTDD es una tcnica usada durante el desarrollo. La idea bsica detrs del TDD es que uno debe de escribir las pruebas primero, antes de escribir una sola lnea de cdigo de la aplicacin. Pero ojo, Cmo sabes que poner en las pruebas si no tienes el cdigo de la aplicacin a la vista? Bueno, ese es el punto. En TDD se escribe la prueba para comprobar la funcionalidad de lo previsto y luego se escribe el cdigo para que coincida. Cuando uno inicia y tiene su primer conjunto de pruebas, todas ellas (obviamente) fallan. Al escribir el cdigo de la aplicacin, nuestro trabajo estar en luz verde y todos los casos de prueba pasarn. Este mtodo nos permite centrarnos ms en los primeros requerimiento, en lugar de perdernos en el cdigo.