← Ir a portada

Frameworks de Tests Doubles en PHP (I)

Introducción

es el framework de test unitarios para , más utilizado en la actualidad. Su primera versión (1.0) fue lanzada el día 1 de diciembre del 2001 (historia visual de PHPUnit http://sebastian-bergmann.de/archives/910-Visualization-of-PHPUnit-Development.html), y actualmente tenemos las versiones 3.5 y 3.6 (acaba de salir hace poco la 3.7, que solo soporta PHP 5.3).

Sebastian Bergmann, el creado del framework, realizo PHPUnit como un framework de la familia xUnit (inspirados en el framework de Kent Beck SUnit). Como otros frameworks de testing, PHPUnit usa aserciones para verificar el comportamienteo de la “unidad de código”, que estamos probando.

Entre las características de PHPUnit, se encuentran:

Os recomiendo que le echéis un vistazo a la documentación.

A pesar de comenzar con PHP en el 2002, no descubrir la existencia de este, ni de Simple Test (otro framework que ha quedado un poco en el olvido http://www.simpletest.org/) hasta el 2006. Pero como siempre te dejas llevar por el día a día, y siempre lo dejaba para otro momento.

Cuando empecé a intentar hacer con PHP, fue elegir como framework a PHPUnit. Utilice como guía la documentación oficial, y entradas de blogs para dar los primeros pasos, pero en realidad solo hacía que intentar cubrir de test el código que teníamos en la intranet del trabajo.

Realmente cuando empecé algo más serio, fue cuando asistí al curso de Carlos Blé (http://www.carlosble.com/) dio en Valladolid en marzo de 2010 (gracias a EXECYL @execyl y el Colegio Profesional de Ingenieros en Informática de Castilla y León @CPIICyL).

En este curso vi por primera vez los tests doubles (mocks y stubs), utilizando Mockito. Al ser la primera toma de contacto, lo utilice pero no termine de entenderlos (soy un poco lento). A mi vuelta al trabajo, revise lo que traía PHPUnit y lo entendi menos aun. Así que lo deje como pendiente y realizo test sin mocks/stubs (unitario, integración, punta a punta, etc).

Tras una charla realizada por Javier Acero (@jacegu) en Lucen IT (y orquestada por Javier Gamarra @nhpatt, a finales de verano 2011) y tras ver el primer capitulo de ¡Hola TDD! (de Sebastian Hermida @sbastn http://holatdd.com/) decido volver a retomar la tema pendiente del uso de tests doubles. Tomando como ejemplo “Point of Sale” de ¡Hola TDD!, decidí realizar un pequeño estudio sobre frameworks de tests doubles que existen en PHP.

Los test doubles, de forma sencilla, nos sirven para situaciones en las cuales el SUT (System Under Test) necesita reemplazar un objeto real por uno “testeable”. Haciendo un símil con el cine, son los dobles que reemplazan al actor principal en una escena. En las referencias, hay enlaces al libro de Gerard Meszaros xUnit Patterns y dos artículos de Martin Fowler, donde se explica más en detalle y mejor (que recomiendo leer) los tests doubles.

Dentro de los tests doubles, se suele hablar básicamente de (definiciones extraidas de libro Diseño Agil con TDD):

  • Stubs: proporciona respuestas predefinidas, a llamadas hechas durante los tests.
  • Mocks: bjeto preprogramado con expectativas que conforman la especificació́n de cómo se espera que se reciban las llamadas. Son más complejos que los stubs, aunque sus diferencias son sutiles.

Aunque muchos autores, añaden: Spies, Fakes y Dummy (ver definición más completa en las referencias).

Primeros pasos

Con una pequeña búsqueda descubrí, que además del existente en PHPUnit, tenemos:
Tras tener los candidatos, el siguiente paso es ponerse manos a la obra. Para ello comencé a implementar “Point of Sale” (y hacer uso de la cuenta de GitHub).

Utilizar un sistema de control de versiones, era intentar hacer un histórico y una rama para cada implementación. En cada “commit” he intentado que fuera un ciclo TDD (rojo, verde y refactorizar), así poder partir de una situación idónea para afrontar el siguiente paso.

He supuesto que ya se conoce PHP, así como el uso de los PEAR (una forma de distribuir e instalar librerías y aplicaciones en PHP).

Manos a la obra

Básicamente el código realizado en “Point of Sale”, que utiliza Mockito (Java), ha sido nuestra guía para implementar la solución. En el primer test (onBarcode_search_catalog()) vamos a verificar que se ha llamado al método de búsqueda, con unos valores concretos en los parámetros.

El segundo test (onBarcode_show_price()), vamos a verificar tanto la llamada a un método concreto como la devolución de un valor concreto al ejecutar el método.

El código utilizado esta en GitHub, https://github.com/isidromerayo/HolaTDD-001-point-of-sale-PHP-Test-Doubles-frameworks.

PHPUnit Object

https://github.com/sebastianbergmann/phpunit-mock-objects.
En primer lugar comenzamos utilizando los que trae PHPUnit (http://www.phpunit.de/manual/3.6/en/test-doubles.html)

La instalación de PHPUnit y de Mock Object se realiza a través del canal PEAR, por lo cual es muy sencillo. Además al realizar la instalación por defecto, ya esta disponible dentro del include_path de PHP y no es necesario tocar nada más.

Fragmento de código del primer test:

El código es un poco más largo que en Mockito, y tal vez un poco menos entendible (por lo menos para mi).

Se podría cambiar once() por any() y funcionaría igualmente, pero estamos restringiendo que se llame una vez en lugar de cero o más veces.

Ejecución: tenemos un test y una aserción

isidromerayo@nikki:~/projects/PHP/Hola_TDD_001_point_of_sale_PHP/tests$ phpunit
PHPUnit 3.6.7 by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 7.00Mb

OK (1 test, 1 assertion)

Fragmento de código del segundo test

Parece que cada vez va creciendo más el código

Ejecución: tenemos dos test y tres aserciones

isidromerayo@nikki:~/projects/PHP/Hola_TDD_001_point_of_sale_PHP/tests$ phpunit PHPUnit 3.6.7 by Sebastian Bergmann.
Configuration read from /home/isidromerayo/projects/PHP/Hola_TDD_001_point_of_sale_PHP/tests/phpunit.xml
..
Time: 0 seconds, Memory: 6.75Mb
OK (2 tests, 3 assertions)

El uso de PHPUnit Mock Object, nos da la facilidad de estar incluida en PHPUnit.
La documentación evoluciona a la vez que lo hace el framework, y es un proyecto muy consolidado dentro de la comunidad PHP. Un gran trabajo realizado por Sebastian Bergmann.
Comente en twitter, lo complicado que era el uso de los Mocks de PHPUnit y Sebastian me recomendo una solución y finalmente abri un ticket. (https://github.com/sebastianbergmann/phpunit-mock-objects/issues/76)
El último commit al proyecto en GitHub, ha sido del día 7 de enero del 2012.

Mockery

https://github.com/padraic/mockery.
Mockery ha sido pensado como un framework de Mock, para utilizar con cualquier framework de unit testing en PHP. Esta inspirado en flexmock de Ruby y Mockito.

Desarrollado por Padraic Brady contibuidor a Zend Framework, y va por su versión 0.7.x en este momento. Tiene la posibilidad de integrar la librería Hamcrest (librería de matchers para construir expresiones para los test, utilizada en diferentes escenarios. Esta disponible para varios lenguajes http://code.google.com/p/hamcrest/).

Nos permite instalarla a través de su canal PEAR, tiene como dependencia el uso la librería Hamcrest pero es muy sencillo instalarla (también a través de PEAR). Si queremos usar la versión de GitHub, deberemos instalar Hamcrest desde el canal PEAR igualmente.

A la hora de utilizarla, vamos a tener que incluir en nuestro fichero TestHelper.php las siguientes líneas para utilizar Mockery.

require_once 'Mockery/Loader.php';
require_once 'Hamcrest/hamcrest.php';
$loader = new \Mockery\Loader;
$loader->register();

Una vez todo preparado, vamos a ver el código.

Fragmento del primer test:

Ejecución: tenemos un test y cero aserciones.

isidromerayo@nikki:~/projects/PHP/Hola_TDD_001_point_of_sale_PHP/tests$ phpunit
PHPUnit 3.5.15 by Sebastian Bergmann.
.
Time: 0 seconds, Memory: 8.75Mb
OK (1 test, 0 assertions)

Fragmento de dódigo del segundo test

Ejecución: dos test y ninguna aserción.

isidromerayo@nikki:~/projects/PHP/Hola_TDD_001_point_of_sale_PHP/tests$ phpunit
PHPUnit 3.5.15 by Sebastian Bergmann.
..
Time: 0 seconds, Memory: 9.25Mb
OK (2 tests, 0 assertions)

Un gran trabajo, documentado y con varios ejemplos tanto en GitHub como en el propio blog de Pádraic http://blog.astrumfutura.com/tag/mockery/. El inconveniente, es que no nos esta contando las aserciones.

Al cambiar de versión de PHPUnit, durante el tiempo de pruebas me han surgido ciertos problemas. Se ejecutaban los test bien unas veces, y otras se producia “segmentation fault” en lugar de salir errores al ejecutarlos (por el uso de teardown() para destruir el objeto).
La última actualización en la rama master es del 21 de Enero del 2012, y ha sacado un nueva release el día 25 de enero.

Phake

Desarrollador por Mike Lively, como se define en la documentación GitHub (desactualizada) es un framework PHP para proporcionar Mock objects, test doubles y method stubs. Se inspira en la sencillez de la funcionalidad de los mocks de SimpleTest y en Mockito. Tiene la posibilidad de integrar la librería Hamcrest.

Tiene una documentación y unos ejemplos de uso excelente, aunque en GitHub no aparece muy visible la referencia a su página http://phake.digitalsandwich.com/docs/html/
Fácil de instalar y actualizar a través de su propio canal PEAR.

El primer paso es añadir en el TestHelper.php, el fichero del framework

require_once 'Phake.php';

Como puede ser utilizado con cualquier framework de test en PHP, para que cuente las aserciones de nuestro código debemos indicar el cliente a utilizar (esto lo descubri cuando ya tenía todos commits subidos, así que esta en la última versión de la rama).

Phake::setClient(Phake::CLIENT_PHPUNIT);

Vamos a ver como quería el código del primer test:

Similiar a Mockito y muy sencillo

isidromerayo@nikki:~/projects/PHP/Hola_TDD_001_point_of_sale_PHP/tests$ phpunit PHPUnit 3.6.7 by Sebastian Bergmann.
Configuration read from /home/isidromerayo/projects/PHP/Hola_TDD_001_point_of_sale_PHP/tests/phpunit.xml
.
Time: 0 seconds, Memory: 6.75Mb
OK (1 test, 1 assertions)

El código del segundo test, quedaría

Y la ejecución, con dos test y dos aserciones.

isidromerayo@nikki:~/projects/PHP/Hola_TDD_001_point_of_sale_PHP/tests$ phpunit PHPUnit 3.6.7 by Sebastian Bergmann.
Configuration read from /home/isidromerayo/projects/PHP/Hola_TDD_001_point_of_sale_PHP/tests/phpunit.xml
..
Time: 0 seconds, Memory: 6.75Mb
OK (2 tests, 2 assertions)

Un excelente trabajo, sobre todo por la sencillez de código, una extensa y muy cuidada documentación (aunque en GitHub este un poco desfasada y haga referencia a ella de forma poco visible).
El último commit a GitHub ha sido del día 3 de enero del 2012.

Phockito

Framework inspirado en Mockito para PHP, esta desarrollado por Hamish Friedlander de la compañia Silver Stripe (que desarrollan un CMS y Framework con el mismo nombre).
Permite usar los matchers de la librería Hamcrest.

La documentación no indica la forma de instalar, así que utilizaremos “git clone” para instalarlo en nuestro sistema (dentro de /usr/share/php/phockito).

Añadimos en nuestro TestHelper.php

require_once 'phockito/Phockito.php';

El código de nuestro primer test quedaría

Resultados de la ejecución

isidromerayo@nikki:~/projects/PHP/Hola_TDD_001_point_of_sale_PHP/tests$ phpunit PHPUnit 3.6.7 by Sebastian Bergmann.
Configuration read from /home/isidromerayo/projects/PHP/Hola_TDD_001_point_of_sale_PHP/tests/phpunit.xml
.
Time: 0 seconds, Memory: 6.75Mb
OK (1 test, 0 assertions)

Y vamos a por el segundo test

y su ejecución

isidromerayo@nikki:~/projects/PHP/Hola_TDD_001_point_of_sale_PHP/tests$ phpunit PHPUnit 3.6.7 by Sebastian Bergmann.
Configuration read from /home/isidromerayo/projects/PHP/Hola_TDD_001_point_of_sale_PHP/tests/phpunit.xml
..
Time: 0 seconds, Memory: 6.00Mb
OK (2 tests, 0 assertions)

La documentación es la más pobre de los frameworks analizados. Ultimo commit, ha sido realizado el 30 de agosto del 2011. Tiene en la documentación un para de TODOs y parece un poco parado. Otro inconveniente, es el conteo de aserciones que no realiza.

 

Como resumen, he creado unos gist para el primer test y otro para el segundo. ¿suficiente información? Creo que no …. (continua leyendo)


Apuntes relacionados:

3 Respuestas a “Frameworks de Tests Doubles en PHP (I)”

  1. Carlos Ble dice:

    Que currado el artículo Isidro!
    Enhorabuena por unirte al blog de Programania! Me parece genial y este post me servirá mucho cuando vuelva a explicar dobles con PHP.

    Gracias :-)

  2. Excelente artículo, Isidro! Muy buen aporte ;)

  3. Isidro Merayo Castellano dice:

    Gracias, espero que os guste lo que queda de la serie ;)

Deja un comentario