Testing en PHP a través de TDD

A lo largo de la historia del software han ido surgiendo diferentes metodolog√≠as de trabajo. Pero centr√°ndonos en las mas actuales, encontramos la Programaci√≥n orientada a objetivos como una de las mas usadas. Seguida de ella tenemos otras como TDD (Test Driven Design), BDD (Behavior Driven Design) y en √ļltima instancia, no por ello menos importante, DDD (Domain Driven Design).

El objetivo de estas metodolog√≠as no es otro que escribir mejor c√≥digo para permitir a la aplicaci√≥n mantenibilidad, bajos costes y extensibilidad, entre otros. El √ļltimo, est√° cobrando fuerza en los √ļltimos a√Īos con la proliferaci√≥n de entornos como Kubernetes, que nos permiten tener una separaci√≥n virtual entre nodos, sin tener que recurrir a una separaci√≥n f√≠sica entre servidores, como se ven√≠a haciendo hasta el momento.

Que es el testing y porqué deberíamos realizarlo

Una de las partes mas importantes del desarrollo de software es tener un buen dise√Īo. Pero existen otras t√©cnicas que nos permiten dotar de mayor fiabilidad a nuestra aplicaci√≥n, como es el caso de la escritura de Test a diferentes niveles. Nos permiten acotar los casos de fallo bajo diferentes situaciones. La mayor√≠a de las veces, no vamos a contemplar el 100% de los casos, ya sea por errores humanos, falta de an√°lisis de todos los edge cases o por integraciones con servicios de terceros. Este aspecto se vuelve bastante necesario, sobretodo cuando queremos tener alg√ļn mecanismo de integraci√≥n continua, puesto que nos puede ayudar a prevenir posibles problemas, antes de llegar a desplegar la nueva l√≥gica en productivo entre otros.

Existen muchas tipolog√≠as en funci√≥n de la tecnolog√≠a, pero, a grandes rasgos, podemos encontrar 3 tipos de test entre los cuales encontramos los unitarios, los de integraci√≥n y los E2E. Los test unitarios, son la base de la pir√°mide. Son f√°ciles de escribir y de mantener. Resultan muy √ļtiles para testear partes unitarias de nuestro c√≥digo, como su propio nombre indica. Siempre es recomendable tener una base s√≥lida de test unitarios que nos permitan realizar cambios en nuestra aplicaci√≥n para evitar posibles side-effects. Por otro lado, tenemos los de integraci√≥n. Resultan mas costos en tiempo de desarrollo y de mantenimiento. Lo que nos permiten es testear el sistema en conjuntos o grupos, cosa que no podemos hacer con los unitarios. Por √ļltimo tenemos los E2E o UI testing. Estos son lo mas cercano al usuario. Aqu√≠ vamos a hacernos pasar por un usuario real, accediendo a la web, e interactuando con ella. Estos test son muy costosos de realizar y de mantener, puesto que tenemos que buscar patrones que nos permitan identificar el correcto funcionamiento de la plataforma. Entre las tareas mas comunes que pueden realizar est√°n, la de pulsar sobre enlaces, botones o mandar formularios.

TDD

Vamos a usar TDD de forma muy b√°sica para realizar los ejemplos de c√≥digo, la cual cosa nos va a forzar a trabajar con la creaci√≥n de test antes de la implementaci√≥n. Es importante destacar, que el dise√Īo de la caracter√≠stica que queramos desarrollar debe estar planificado de antemano, son cosas complementarias, no excluyentes. Existen diferentes herramientas en PHP para realizar test, como PHPSpec si queremos hacer BDD o PHPUnit. En nuestro caso vamos a usar esta √ļltima para realizar el desarrollo del ejemplo usando TDD de forma muy b√°sica. Las 3 leyes que Robert C. Martin describe al usar esta metodolog√≠a, son las siguientes:

  • No est√° permitido escribir ning√ļn c√≥digo de producci√≥n a menos que haga pasar un test unitario que falle.
  • No est√° permitido escribir m√°s de un test unitario que sea suficiente para fallar; y los errores de compilaci√≥n son fallos.
  • No est√° permitido escribir m√°s c√≥digo de producci√≥n del que sea necesario para hacer pasar un test unitario que falle.

Es importantes destacar el flujo de trabajo que vamos a seguir, en lo que se conoce como Red-Green-Refactor. Nuestro primer paso siempre es hacer un test que falle, como indica la 1¬ļ regla. Es en este momento cuando podemos pasar al segundo paso. Hacer que el test pase, en la mayor√≠a de entornos de test esto se indica con verde. Hay que tener en cuenta, que como indica la 2¬ļ regla, nuestro prop√≥sito es √ļnica y exclusivamente hacer pasar el test. Hasta ese momento no podemos llevar a cabo ninguna acci√≥n mas. Esto nos va a forzar a seguir siempre ese proceso, de lo contrario, podr√≠amos asumir el resultado viendo la simplicidad del test y provocar problemas mayores en partes complejas de un problema del cual no conocemos todos sus edge-cases. Cuando tenemos la 2¬ļ fase terminada, es cuando podemos refactorizar la l√≥gica y/o test hasta que quede acorde al dise√Īo previamente deseado. Lo que se busca de esta forma es ir de afuera hacia adentro, buscando los casos mas comunes a los edge-cases mas complejos. Hay que tener en cuenta que no debemos escribir nunca un test que ya est√© contemplado en alguno previamente creado.

Caso de uso

En primer lugar vamos a instalar phpunit para empezar a usarlo de la siguiente forma:

También lo podemos instalar en formato de ejecutable (PHAR) para que no dependa de nuestro proyecto, como explican en la documentación. Si lo instalas de esta forma, es importante que configures el autoloading de
composer de forma manual como explican en el siguiente enlace.

Ahora lo que vamos a hacer es crear el archivo de configuración de PHPUnit con un comando que nos provee:

Como se observa en la imagen, tendr√°s que indicarle en que directorios se encuentran el autoload.php de composer, el directorio de tests y el de src. En nuestro caso lo hemos dejado por defecto, porque vamos a usar esa estructura de directorios:

Nuestro primer paso va a ser crear el test para nuestro sencillo objeto Queue donde meter elementos. La forma de ejecutar los test, en nuestro caso siempre va a ser la misma.

Aqu√≠ hemos a√Īadido el flag ¬ę–colors¬Ľ para ejecutar el test, queremos ver con claridad ese color rojo tan caracter√≠stico de nuestra primera fase del flujo.

Ahora vamos a crear la lógica mínima para que el test pase de rojo a verde. Te animamos a que investigues para que puede servir esa anotación covers.

Cabe resaltar, que ni tan siquiera hemos a√Īadido constructor, queremos la m√≠nima expresi√≥n que haga verde nuestro test. Ahora vamos a volver a ejecutarlo para ver el resultado:

Ya tenemos nuestro primer test en verde (hemos a√Īadido el ¬ęuse¬Ľ de la clase Queue al test para que funcione, evitamos a√Īadir el snippet por redundacia), en este caso, nos vamos a saltar el proceso de refactor, puesto que no queremos a√Īadir ning√ļn requerimiento al construir el objeto.

Como siguiente feature, lo que vamos a hacer es dotar a nuestro Queue de dos nuevos comportamientos, a√Īadir un elemento, y obtener todos los elementos. Para ello vamos a hacer uso de una interfaz, queremos que nuestros compa√Īeros implementen su propio objeto Queue seg√ļn sus necesidades, pero no que modifique nuestra implementaci√≥n. Volvemos al flujo comentado anteriormente, creamos un nuevo test:

Como podrás observar, el test está rojo, es el momento de convertirlo en verde, para ello vamos a crear la interfaz y hacer que nuestro Queue la implemente (como hemos hecho anteriormente con la clase, incluimos el use en el test y creamos una interfaz vacía):

Con esto hemos conseguido lo que nos proponíamos, hacer pasar el test. Lo siguiente que vamos a hacer es dotar a la interfaz de los comportamientos que hemos comentado anteriormente. Recordemos el proceso, crear el test y acto seguido incluir la implementación. Nosotros vamos a incluirlo en ese orden todo a la vez, puesto que ya conocemos el proceso y queremos mostrarlo todo de forma mas compacta:



Recuerda que solo necesitamos hacer pasar los test, hasta ese momento no podemos implementar lógica, por eso estamos pasándole al método add un string vacío.
Si todav√≠a no lo has hecho, es el momento de que ejecutes los test en modo –testdox. Vamos a ver el sentido que tiene poner nombres con significado a cada uno de los test.

Ahora vamos a implementar la lógica pero con un test, recuerda el proceso Red-Green-Refactor, es muy importante:

Te dejamos algunos consejos a la hora de crear tests:

  • Intenta evitar argumentos con valores primitivos, extr√°elos a constantes para dotar de mayor legibilidad al c√≥digo.
  • Nosotros hemos usado la abreviaci√≥n sut para hacer referencia a SubjectUnderTest.
  • Puedes hacer uso del assert null para casos en los que el m√©todo no tiene valores de devoluci√≥n.

Conclusión

Hemos hecho un breve recorrido de como testear un peque√Īo ejemplo siguiendo la metodolog√≠a TDD. Es importante que te adhieras a la metodolog√≠a que mejor se adapte a las necesidades del negocio, contando con el apoyo del equipo, para dotarlo de una mayor cohesi√≥n. Nos hemos dejado cosas importantes, que te invitamos a investigar, como la cobertura de test. PHPUnit tiene una peque√Īa funcionalidad para generar informes en gran variedad de formatos. Con esto podemos detectar las partes con menor cantidad de test para solventarlo. Es interesante usarla, pero el 100% de cobertura no nos asegura que no exista ninguna inconsistencia en nuestro nuestro c√≥digo, es una medida mas de la calidad de los test, hay ponerla en contexto con el resto de t√©cnicas.

Por otro lado, hay herramientas que nos permiten realizar mutaciones en los test, descubriendo posibles partes débiles o mal testeadas, Infection es una librería muy interesante. Realizando mutaciones sencillas, es capaz de encontrar casos que previamente no se había contemplado. Si te interesa, puedes usar los mokcs que proporciona PHPUnit o librerías como Mockery. La unión de ambas técnicas, el code coverage con los mutation test, provee de una base de test unitarios muy sólida y bastante recomendable para seguir desarrollando la arquitectura del código. Siempre hay que realizar una evaluación a posteriori de todos los casos de uso de nuestra aplicación.

Comparte
Share on facebook
Share on twitter
Share on linkedin
¬ŅQuieres m√°s informaci√≥n?
Ponte en contacto con nosotros.
Jordi Mahiques
Jordi Mahiques
Backend developer - Departamento de desarrollo

Enviar Comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

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

Suscríbete a nuestra newsletter
para estar al día en el mundo online
¬ŅTienes alguna incidencia?

Cuéntanos qué ocurre
y nos pondremos con ello lo antes posible.

Este sitio está protegido por reCAPTCHA, y la Política de privacidad y Términos de servicio de Google.
¡Cuéntanos tus ideas!
+34 96 653 19 14
info@acceseo.com
Este sitio está protegido por reCAPTCHA, y la Política de privacidad y Términos de servicio de Google.