Agregar PHPUnit a tu proyecto PHP con composer
Published: 2020-06-29 04:57:42

Con el tiempo, los proyectos y librerías tienden a evolucionar ya sea para agregar nuevas funcionalidades o simplemente para mejorar el rendimiento y desempeño. Una manera de garantizar que nuestra librería se mantendrá estable a lo largo de éstas mutaciones es a través de una serie de pruebas. 

Una prueba es un fragmento de código que se asegura de que una función o clase haga exactamente lo que se le designó.

PHP Unit es un framework que nos permite crear y ejecutar pruebas para nuestro código de una manera muy sencilla, a demás de brindarnos herramientas para revisar que tanto porcentaje de nuestro código ha sido probado (coverage) y la complejidad de nuestro código (complexity).

Para ésta guia utilizaremos el código creado en el blog "Inicializa un proyecto PHP usando composer" para establecer los primeros pasos, si deseas leerlo puedes [ingresar aquí](https://dev.to/javleds/inicializa-un-proyecto-php-usando-composer-43bb), de lo contrario, te dejo el [repositorio en github](https://github.com/javleds/inicio-php-composer), el cual puedes clonar o descargar como zip.

La estructura inicial de nuestro proyecto debería verse de la siguiente manera:

```
fabulosa_libreria/
|--- composer.json
|--- README.md
|--- src/
	|--- funciones_globales.php
	|--- Mensajes.php
	|--- Utilerias/
		|--- Calculadora.php
```


Primero que nada debemos instalar PHPUnit, teniendo composer esto se fácilita bastante, solo debemos ejecutar el siguiente comando:

```bash
composer require --dev phpunit/phpunit
```

>  Se utiliza la bandera `--dev` para indicarle a composer que la libreŕia solo e utilizará en entornos de desarrollo.

Una vez ejecutado éste comando, composer se encargará de realizar la instalación de todos los elementos necesarios. Estos elementos se encontrarán situados en la carpeta `vendor` . 

> Si estás utilizando algún sistema de control de versiones, es importante que agregues la carpeta vendor a los archivos ignorados, ya que con composer se pueden volver a instalar fácilmente.

Si queremos asegurarnos de que phpunit se ha instalado correctamente, podemos revisar el archivo `composer.json` el cual debería tener una nueva entrada llamada `require-dev` con algo similar a lo siguiente:

```json
"require-dev": {
  "phpunit/phpunit": "^8.5"
}
```

En esta ocasion, composer instaló la versión 8.5 de phpunit, ésto puede variar dependiendo de la fecha en la que se lleve a cabo ésta guia.

## Regsitrar carpeta de pruebas en composer 

Para organizar nuestro proyecto crearemos la carpeta `pruebas` en ella colocaremos de manera ordenada  todas las pruebas que se irán crando a lo largo del desarrollo, ésta carpeta estará registrada en composer para poder utilizar los nombres de espacio de nuestro proyecto sin complicaciones.

Para ello, creamos la carpeta en la raiz de nuestro proyecto, quedando una estructura similar a la siguiente:

```
fabulosa_libreria/
|--- composer.json
|--- README.md
|--- src/
	|--- funciones_globales.php
	|--- Mensajes.php
	|--- Utilerias/
		|--- Calculadora.php
|--- pruebas/
```

Ahora, registraremos la carpeta `pruabas` con el namespace `Javleds\Pruebas`, para ello, editaremos el  archivo `composer.json`, y agregaremos la entrada `autoload-dev.psr-4`,  de la siguiente manera:

```
{
  "name": "javleds/fabulosa-libreria",
  "description": "Mi fabulosa librería",
  "type": "library",
  "authors": [
    {
      "name": "Javier Ledezma",
      "email": "contact@javierledezma.com"
    }
  ],
  "version": "1.0",
  "license": "MIT",
  "autoload": {
    "psr-4": {
      "Javleds\\FabulosaLibreria\\": "src/"
    },
    "files": [
      "src/funciones_globales.php"
    ]
  },
  "autoload-dev": {
    "psr-4": {
      "Javleds\\Pruebas\\FabulosaLibreria\\": "pruebas/"
    }
  },
  "require-dev": {
    "phpunit/phpunit": "^8.5"
  }
}

```
> Nota: En el ejemplo utilizamos el `namespace` Javleds\FabulosaLibreria, pero tu deberias utilizar un nombre de espacio único para tu proyecto compuesto de `USUARIO_GIT\NOMBRE_LIBRERIA`.

A diferencia de la entrada `autoload`, las clases y archivos registrados en la entrada `autoload-dev` solo estarán disponibles para entornos de desarrollo.

Es tiempo de revisar si phpunit funciona correctamente, para ello, crearemos una prueba para la función`elementoAleatorio` que se encuentra en el archivo `funciones_globales.php`, esta función luce así:

```php
if (! function_exists('elementoAleatorio')) {
    /**
     * @param array $elementos
     *
     * @return mixed
     */
    function elementoAleatorio($elementos)
    {
        $valorMaximo = count($elementos) - 1;
        $indiceAleatorio = rand(0, $valorMaximo);
        return $elementos[$indiceAleatorio];
    }
}
```

La función recíbe un arreglo con elementos de cualquier tipo y regresa un elemento de manera aleatoria, nuestra prueba se encargará de asegurarse que la función `elementoAleatorio` nos regresa siempre algún valor, y cuyo valor debe estar contenido en ese arreglo. Crearemos el archivo `pruebas/FuncionesGlobalesTest.php` con lo siguiente:

```php
<?php

namespace Javleds\Pruebas\FabulosaLibreria;

use PHPUnit\Framework\TestCase;

final class FuncionesGlobalesTest extends TestCase
{
    public function testElementoAleatorioDebeRegresaUnValor()
    {
        $elementos = [1, 2, 3, 4, 5];
        $elemento = elementoAleatorio($elementos);

        $this->assertNotNull($elementos);
        $this->assertContains($elemento, $elementos);
    }
}
```

Las prubas deben extender de la clase `PHPUnit\Framework\TestCase`, en ésta clase se encuentan todas las utilidades que nos brinda PHPUnit para crear pruebas.

La prueba que creamos inicializa un arreglo con los valores del 1 al 5, posteriormente manda a llamar la función `elementoAleatorio` y revisa que:

1. El valor regresado no sea nulo
2. Que el valor regresado por la función exista dentro del arreglo inicial.

Ahora, llamaremos le indicaremos a PHPUnit que queremos ejecutar esa prueba con el comando:

```
./vendor/bin/phpunit pruebas/
```

Este comando buscará dentro de la carpeta `pruebas` todos los archivos que terminen con la palabra `Test.php` y ejecutará las funciones que comiencen con el sufijo `test` o tengan la anotación `@test`.

Si todo sale bien, podremos ver un resultado similar al siguiente:

```
PHPUnit 8.5.8 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 24 ms, Memory: 4.00 MB

OK (1 test, 2 assertions)
```

Esto quiere decir que se ejecuto 1 prueba, de las cuales 1 cumple con lo establecido, en ella se realizaron 2 asersiones (la del valor nulo y la del valor existe en el arreglo).

## Creando un archivo de configuración de PHPUnit

Ahora bien, ya sabiendo que podemos ejecutar pruebas, lo ideal sería configurar `PHPUnit` para no tener que específicar las rutas de nuestras pruebas en cada ocasión, a demás de otra serie de configuraciones. Para ello crearemos el archivo `phpunit.xml` en la raíz del proyecto con lo siguiete:

```xml
<?xml version="1.0" encoding="UTF-8"?>

<phpunit 
    bootstrap = "vendor/autoload.php"
    colors = "true"
    convertErrorsToExceptions = "true"
    convertNoticesToExceptions = "true"
    convertWarningsToExceptions = "true">

    <testsuites>
        <testsuite name="Pruebas del proyecto">
            <directory>pruebas</directory>
        </testsuite>
    </testsuites>

    <filter>
        <whitelist>
            <directory suffix=".php">src/</directory>
        </whitelist>
    </filter>
</phpunit>
```

El directorio debería quedar de la siguiente manera:

```
fabulosa_libreria/
|--- composer.json
|--- README.md
|--- src/
	|--- funciones_globales.php
	|--- Mensajes.php
	|--- Utilerias/
		|--- Calculadora.php
|--- pruebas/
|--- phpunit.xml
```

En el archivo podemos encontrar las configuraciones necesarias para nuetro proyecto, pero en cuanto se requieran más configuraciones, puedes irlas agregando acorde a la documentación oficial, [más información aquí](https://phpunit.readthedocs.io/es/latest/configuration.html)

En cuyo documento tenemos:

- `phpunit.bootrstrap`: Le indicamos a PHPUnit que debe cargar el archivo `vendor/autoload.php` antes de comenzar a ejecutar las pruebas, por si no estás muy familiarizado con `composer`, el archivo `vendor/autoload.php` contiene el registro de todas clases y funciones de las que depende nuestro proyecto, incluyendo las que registramos manualmente en las seccion `autoload` del archivo `compoaser.json`, este archivo se crea automáticamente por `composer` cada que instalamos una nueva dependencia y/o cuando ejecutamos el comando `composer dump-autoload`.
- `phpunit.colors`: Le indicamos a PHPUnit que al terminar nos indique con colores rojo y verde el resultado de nuestras pruebas. Rojo cuando hay errores o verde cuando todo se ejecutó correctamente.
- `phpunit.convertErrorsToExceptions`: Le indicamos a PHPUnit que convierta cualquier error en una ecepción.
- `phpunit.convertNoticesToExceptions`: Le indicamos a PHPUnit que convierta cualquier evento de información en una ecepción.
- `phpunit.convertWarningsToExceptions`: Le indicamos a PHPUnit que convierta cualquier alerta de PHP en una ecepción.
- `testsuites.testsuite`: Le indicamos a PHPUnit cual es el directorio que contiene nuestras pruebas, en este apartado se pueden crear grupos de pruebas dependiendo de su función, pero en éste ejemplo solo tenemos una para todo el proyecto.
- `filter.whitelist`: Esta configuración le indica a PHPUnit que los archivos contenidos en el directorio `src` deberán ser contemplados para calcular la covertura de pruebas.

 

Ahora bien, para ejecutar nuestra suite de pruebas solamente debemos ejecutar el comando:

```bash
./vendor/bin/phpunit
```

De manea automática, este comando leerá el archivo `phpunit.xml` y ejecutara las `suites` declaradas allí, ya no será necesario indicarle nada más.

## Covertura de código

Otra ventaja de tener definidas `suites` de pruebas en el arcivo `phpunit.xml` es que podemos generar un reporte con la covertura de pruebas que tiene nuestro código, PHPUnit nos brinda diversos formatos para éste reporte, yo te recomiendo el formato HTML, quien te genera una página web con los resultados.

Para genera ese reporte solo debemos agregar la bandera `--coverage-html` con el nombre del directorio en el que se guargarán los archivos, ejemplo:

```bash
./vendor/bin/phpunit --coverage-html coverage
```

Este comando ejecutara todas las pruebas del archivo `phpunit.xml` y a demás nos creará la carpeta coverage con algunos archivos, nosotros abriremos con nuestro navegador de preferencia el archivo `coverage/index.html` y debe mostrarnos de manera interactiva que archivos ya tienen pruebas y qué procentaje de ese código ha sido probado. Si seguiste el tutorial con el código del blog "Inicializa un proyecto PHP usando composer", entonces, deberías poder ver algo similar a lo siguiente:

![coverage](https://dev-to-uploads.s3.amazonaws.com/i/ovo94fk8bln86a00pcn6.png)

En dónde el reporte nos indica que el archivo `src/funciones_globales.php` tiene un 100% de covertura, sin embargo, no tenemos pruebas definidas para `src/Mensajes.php` y `src/Utilerias/Calculadora`.

## Conclusión y tips

Agregar o no pruebas siempre ha sido un tema de controversia, en mi experiencia, las pruebas son necesarias si quieres que tu proyecto sea escalable y esté pensado para ser un proyecto colaborativo. Las pruebas permiten que tu código crezca y a demás se pueda refactorizar con el tiempo sin temor a dañar las partes de código que ya funcionan, a demás de que te permiten integrar herramientas de integración y despliegue continuo.

Tips:

- Antes de comenzar a escribir código, se recomienda diseñar una prueba primero, posterior a la prueba, comenzar a desarrollar la funcionalidad que esperas siempre se apegará al proposito original. Realizar la prueba y posteriormente el código es una buena práctica y una métodología conocida como TDD (Test Driven Design).
- No te preocupes por tener el 100% de covertura de tu código, enfocate en probar aquellas cosas que requieran procesamiento de la computadora, por ejemplo, operaciones matematicas complejas, creaciónes de archivos, inserciónes y ediciones de la base de datos, entre otros. Un 80% de covertura es muy aceptable.
- Piensa tus pruebas orientadas a casos de uso o a tus reglas de negocio.
- Elabora pruebas sencillas de leer, ninguna prueba debe ser más compleja que el código que se está probando.
- Si tu proyecto consume API's de terceros, procura que ninguna de tus pruebas utilice esos servicios, hay técnicas para "simular" una llamada a una API, estas se conocen como mocks o stubs, puedes revisar un poco más aquí.
- Dentro de tu carpeta de `pruebas/` intenta ordenar el código de tal manera que sea un espejo de la carpeta `src/`, esto facilitará el mantenimiento de las pruebas y mantendrá el código organizado, un ejemplo para el proyecto actual sería:
  ```
  fabulosa_libreria/
  |--- src/
  	|--- funciones_globales.php
  	|--- Mensajes.php
  	|--- Utilerias/
  		|--- Calculadora.php
  |--- pruebas/
  	|--- FuncionesGlobalesTest.php
  	|--- MensajesTest.php
  	|--- Utilerias/
  		|--- CalculadoraTest.php
  ```


¡Gracias por tu tiempo!, esta guia es un poco larga, pero espero que te sirva mucho en tu día a día, te invito a que agregues pruebas a tus proyectos y si ya has trabajdo con PHPUnit te invito a que nos cuentes tu experiencia en los comentarios. 

¡Hasta la próxima!