Unittest en php con Lime
| Author: | Hugo Ruscitti |
|---|---|
| Date: | Febrero 2011 |
Escribir pruebas de unidad es muy importante, nos da la posibilidad de asegurar el funcionamiento de nuestro sistema y hacer cambios a futuro.
Lamentablemente, a pesar de las ventajas que ofrecen las pruebas de unidad, comenzar a utilizarlas y adquirir el hábito de escribirlas es un poco difícil.
En este artículo quiero hablar de una herramienta para hacer pruebas muy sencilla. Y es tan sencilla que nos ayudará a superar esa resistencia inicial a escribir test.
Lime
Lime es la herramienta mas sencilla para escribir pruebas de unidad que he visto. Las pruebas con lime son muy directas, secuenciales y fáciles de seguir.
Lime ha sido desarrollada originalmente por Fabien Potencier para el proyecto symfony. Pero actualmente está desacoplada de symfony, y se puede integrar con mucha facilidad a otros proyectos.
Instalación
Para instalar lime solamente tenemos que descargar el archivo lime.php y colocarlo en un lugar accesible para nuestro proyecto:
wget "http://trac.symfony-project.org/browser/tools/lime/trunk/lib/lime.php?format=raw" -O lime.php
Un script de ejemplo
Si tenemos nuestros test en el mismo directorio que el archivo lime.php, podemos escribir una prueba como esta:
Archivo test.php:
<?php require 'lime.php'; $t = new lime_test(2); $t->comment('Iniciando las pruebas'); $t->is(2+2, 4, "sabe sumar correctamente."); $t->ok(1 == 1, "estos dos numeros son iguales."); ?>
Es decir, tenemos que indicar la ruta del archivo lime.php y luego crear un objeto de la clase lime_test, indicando la cantidad de pruebas que vamos a realizar.
Luego solo queda correr la prueba usando el intérprete de php en consola:
php test.php
Métodos
Lime expone varios métodos para hacer las pruebas, esta es una lista de todos los que se incluyen.
- diag($msg)
- Imprime un mensaje que se incluye en la salida.
- ok($test[, $msg])
- Verifica una condición y pasa si el valor es true.
- is($value1, $value2[, $msg])
- Compara dos valores y pasa si los dos son iguales.
- isnt($value1, $value2[, $msg])
- Compara dos valores y pasa si los dos son diferentes.
- like($string, $regexp[, $msg])
- Verifica una cadena contra una expresión regular.
- unlike($string, $regexp[, $msg])
- Verifica una cadena esperando que no sea igual a una expresión regular.
- cmp_ok($value1, $operator, $value2[, $msg])
- Compara dos argumentos con un operador.
- isa_ok($variable, $type_or_class[, $msg])
- Analiza el tipo de un argumento, pasa solo si coincide con el tipo o clase indicado.
- can_ok($object, $method[, $msg])
- Verifica la disponibilidad de un método en un objeto.
- is_deeply($array1, $array2[, $msg])
- Verifica que dos arrays tengan los mismos valores.
- include_ok($file[, $msg])
- Valida que un archivo existe y que se puede incluir correctamente.
- fail([$msg])
- Siempre falla. Útil para verificar excepciones.
- pass([$msg])
- Siempre pasa correctamente.
- skip([$msg, $n])
- Cuenta como $n test, útil para test condicionales.
- todo([$msg])
- Cuenta como un test. Ideal para test que se tienen que escribir.
- comment($msg)
- Muestra un mensaje pero sin correr una prueba.
- error($msg)
- Muestra un mensaje de error pero sin correr una prueba.
- info($msg)
- Imprime un mensaje de aviso pero sin correr un prueba.
Otro ejemplo sencillo
Imagina esta situación, tenemos 2 clases, una representa platos de un restaurant y la otra un pedido:
Agreguemos un poco de lógica: nos gustaría poder estimar el precio de los pedidos, para eso cada plato tiene un precio:
En lugar de comenzar por el código de la implementación, hagamos un test:
<?php require 'lime.php'; $t = new lime_test(3); $plato_mila = new Plato("Suprema napolitana", 20); $plato_ensalada = new Plato("Ensalada de salmón", 25); $pedido = new Pedido(); $t->diag("Verificamos que el precio inicial es 0."); $t->is($pedido->precio(), 0, "El pedido sin platos no tiene precio."); $t->diag("Agregamos un plato de milanesa al pedido"); $pedido->agregar($plato_mila); $t->is($pedido->precio(), $plato_mila->precio(), "Un pedido de un plato vale lo mismo que el plato."); $t->diag("Ahora agregamos una ensalada"); $pedido->agregar($plato_ensalada); $precio_esperado = $plato_mila->precio() + $plato_ensalada->precio(); $t->is($pedido->precio(), $precio_esperado, "El pedido vale lo mismo que los dos platos."); ?>
Naturalmente los test van a fallar, y está bien, el solo hecho de escribirlos está bueno para tener una idea de cómo se tienen que comportar los objetos.
Note
Esto es una aproximación a una técnina llamada TDD (desarrollo orientado por pruebas) que consiste en comenzar escribiendo el código de pruebas y luego la implementación.
Es una buena idea, nos asegura que tenemos que pensar en la interfaz primero, dando sentido a la encapsulación y además nos asegura que vamos a escribir los test si o si...
Entonces, con los test en una terminal podemos comenzar a implementar el código que los haga funcionar. Por ejemplo, en medio del desarrollo obtenía esto:
Luego, el código completo de la implementación me quedó así:
<?php Plato { public function __construct($nombre, $precio) { $this->nombre = $nombre; $this->precio = $precio; } public function precio() { return $this->precio; } } class Pedido { var $lista_de_platos = array(); public function agregar(&$plato) { $this->lista_de_platos[] = $plato; } public function precio() { $acumulado = 0; foreach ($this->lista_de_platos as $plato) $acumulado += $plato->precio(); return $acumulado; } } ?>
y el resultado de las pruebas!!
Invocando las pruebas desde symfony
Si usamos lime desde un proyecto de symfony, podemos invocar al siguiente comando y se ejecutarán todas las pruebas a la vez:
php symfony test:unit
Esto es una atajo, ten en cuenta que de todas maneras puedes iniciar las pruebas usando el intérprete de php.
Usando datos
Si tus test usan el modelo de datos, es importante indicar el nombre de la aplicación y el entorno de pruebas:
$configuration = ProjectConfiguration::getApplicationConfiguration('auditoria', 'dev', true);
new sfDatabaseManager($configuration);
También se puede especificar un lote de datos inicial (en inglés conocido cómo fixture):
$t->diag("Cargando lote de datos:");
Doctrine_Core::loadData(dirname(__FILE__).'../../data/fixtures');
Conclusión
Lime es una buena herramienta para que puedas comenzar a escribir test. Es fácil de aprender, ocupa poco espacio (¿o pesa?), y se puede integrar a cualquier proyecto php.
Espero que te resulte de utilidad, Saludos.


