> Manuales > Manual de Testing en Laravel

Explicamos cómo puedes crear tus primeros tests en una aplicación Laravel, ya sean tests unitarios o tests de funcionalidades o features.

Cómo crear tus primeros test en Laravel

En el anterior artículo de este manual aprendimos una serie de generalidades sobre cómo se organizan y se ejecutan las pruebas de las aplicaciones desarrolladas con Laravel. En esta ocasión vamos a comenzar una parte un poco más práctica, explicando cómo puedes crear tus primeros test.

En este artículo vamos a crear las primeras pruebas en Laravel:

Cómo crear archivos de test en Laravel

Para crear archivos que alojen las pruebas de los test en tu aplicación Laravel dispones de un comando de Artisan que te permite crear el esqueleto básico del código.

Si no indicamos lo contrario, cualquier archivo de pruebas se creará dentro de la carpeta tests/Feature, construyéndose como una prueba funcional.

php artisan make:test MiPruebaTest

Si queremos que el archivo de test sea configurado como una prueba unitaria, en lugar de una prueba de funcionalidad, entonces tenemos que agregar el flag --unit al comando de Artisan.

php artisan make:test MiPruebaTest --unit

Si creas dos archivos de prueba, tanto de funcionalidad como unitaria, verás que el código de base que se propone es ligeramente diferente.

Crear un test de prueba unitaria

Vamos a crear un primer archivo de prueba unitaria, que resultaría un poco más sencillo para empezar. Obviamente aquí necesitarás tener un conocimiento básico sobre PHPUnit, ya que es el framework que usarás para poder ejecutar tus pruebas unitarias.

Como hemos dicho, Laravel también es capaz de ejecutar las pruebas con el framework Pest, pero en este manual vamos a trabajar siempre con PHPUnit.

Quiero crear un test para una clase que se llamara TimeValidator. Será un validador de tiempo especial, que necesito para un proyecto que construye horarios. El formato de las horas que quiero validar es una cadena "hh:mm", siempre con notación de 24h.

Mi clase TimeValidator debe recibir por constructor la cadena a validar. Luego debe haber un método que diga si el horario es válido o no. Todavía no tengo la clase creada, pero aún así puedo comenzar por generar mi primer test, trabajando bajo la mecánica propuesta por el TDD.

Primero crearé el archivo para mi prueba unitaria con el siguiente comando de Artisan:

php artisan make:test ValidateTimeTest --unit

Luego usaré el siguiente código para la prueba:

<?php

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\Test;
use App\Lib\TimeValidator;

class ValidateTimeTest extends TestCase
{
    #[Test]
    public function it_validates_correct_time_format()
    {
        $validator = new TimeValidator('08:00');

        $this->assertTrue($validator->isValid());
    }
}

Cómo marcar los métodos de test

Aquí hay un detalle que quiero señalar, aunque es algo que está más relacionado con PHPUnit que con el testing en Laravel en particular.

Puedes haber apreciado que el código anterior varía un poco con respecto al típico código de test que podemos encontrar en otros manuales, o incluso al código de test generado por Artisan de manera predeterminada.

Como probablemente sepas, en PHPUnit todos los métodos de clases de prueba que testean alguna cosa deben comenzar con la palabra "test". Si no lo haces se considerará un método de utilidad y no se ejecutará cuando se corran los tests.

En nuestro caso el método no comienza por "test" y para conseguir que PHPUnit lo procese hemos utilizado una marca que se llama "Atributo" en PHP. Esa marca que quiero que veas es el código #[Test] que precede al método de prueba.

Esos atributos son algo que viene con PHP nativo y que PHPUnit saca partido desde sus últimas versiones. Este tipo de código, que no hace nada más que marcar ciertas funciones o características en el código. Para que nos entendamos, a los Atributos de PHP se les conoce normalmente como "Anotaciones" o "Decoradores" en otros lenguajes.

PHP ha presentado los atributos en versiones modernas del lenguaje. Si se me permite dar mi opinión, el nombre de "Atributo" no parece muy acertado, ya que normalmente llamamos "atributos" a las "variables" que tienen los objetos, con datos que definen su estado. Por tanto puede confundir un poco usar la misma palabra para cosas tan distintas.

Si decides decorar tus métodos de test con el atributo Test, sólo debes importarlos para que puedan procesarse, colocando el correspondiente use para importarlo.

use PHPUnit\Framework\Attributes\Test. 

Ejecutar el primer test Laravel

Ahora puedes ejecutar la prueba con el comando de Artisan que mencionamos en el artículo anterior.

php artisan test

Obviamente, mi prueba va a fallar porque ni siquiera he empezado a construir la clase ValidateTime. No pasa nada, es un primer paso. Ahora, siguiendo la práctica de TDD tengo que construir el código mínimo necesario para que esta prueba no falle.

<?php

namespace App\Lib;

class TimeValidator {

    private string $time;

    public function __construct(string $time) {
        $this->time = $time;
    }

    public function isValid(): bool {
        return true;
    }
}

Fíjate que en mi aplicación las clases de utilidad están en el namespace App\Lib. Esto quiere decir que el archivo de mi clase TimeValidator tendrá que estar en la ruta app/Lib/TimeValidator.php.

Una vez hemos creado ese archivo con el código mínimo anterior, si volvemos a ejecutar los test habremos obtenido verde, como que se han podido validar correctamente (solo ten en cuenta que debes haber importado correctamente la clase TimeValidator con el correspondiente use calificando su namespace).

De nuevo, obviamente nuestra clase no está ni mucho menos completa, pero este es el modo de trabajo recomendado por TDD, qué es la escuela más extendida para el trabajo en todo lo que respecta al desarrollo de software dirigido por pruebas. Ya quedaría por tu parte completar la clase y realizar todos los tests necesarios para conseguir asegurar que está funcionando correctamente en todas las posibles circunstancias probables.

Hacer un test de funcionalidad (feature)

Ahora vamos a ver cómo se realizaría un test de feature, para los cuales el proceso cambia un poco con respecto a la realización de los test unitarios con PHPUnit, ya que aquí el framework si nos ayudará bastante por medio de sus métodos de utilidad para tests.

Un test de funcionalidad en Laravel permite verificar que una parte de tu aplicación funciona como esperas. Por ejemplo probarás cosas como que al acceder a través de una ruta HTTP obtienes una respuesta determinada. Para poder ejecutar este tipo de tests Laravel pone en marcha el motor que ofrece el propio framework, que es capaz de responder a rutas, ejecutar código de los controladores, generar el código HTML de las vistas y todo eso que ya sabemos.

Si quisiéramos verificar que la home está funcionando correctamente podríamos crear un test con el siguiente comando de Artisan:

php artisan make:test HomePageTest

De momento vamos simplemente a analizar el código que se ha generado en esta clase de test.

<?php

namespace Tests\Feature;

use Tests\TestCase;
use PHPUnit\Framework\Attributes\Test;

class HomePageTest extends TestCase
{

    #[Test]
    public function the_home_page_returns_a_200_http_status_code(): void
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

Este test de feature realiza algo muy comprensible a simple vista. Consiste en verificar que una ruta devuelve un código de respuesta determinado cuando se consulta. Sin embargo, realmente, aunque el código sea bastante simple, en realidad esta prueba involucra muchos factores que deben de funcionar correctamente, como el sistema de rutas, controladores, devolución de los códigos HTTP correctos para la respuesta, etc. Por eso es algo más que un simple test unitario y lo llamamos test de funcionalidad o prueba funcional.

Si ejecutamos el test va a funcionar perfectamente, ya que cualquier aplicación Laravel recién instalada responde a una ruta de home (raíz de la aplicación) y el código HTTP de respuesta predeterminado que genera la aplicación es el 200.

Si todavía no entiendes del todo el código de test de feature anterior no te preocupes, porque solo lo hemos dejado caer. Por supuesto, volveremos sobre ellos en breve.

Debuggear las respuestas de tests

Cuando hacemos un request desde el código de pruebas hacemos aserciones para comprobar si cumple ciertas premisas. Si no se cumplen, la propia salida de la ejecución de los test nos ofrece información útil que puede orientarnos acerca del fallo que está pudiendo ocurrir. Sin embargo, a veces no es suficiente para entender completamente el escenario de prueba y su salida.

Es por ello que Laravel ofrece algunos métodos que nos permiten debuggear la salida. Esto quiere decir que podemos verificar el código que nos devuelve la vista, así como otros elementos de la respuesta, como las cabeceras del HTTP.

Estos métodos se ejecutan sobre el objeto $response que se genera al ejecutar el escenario a probar:

$response->dump();

Ese método nos mostraría la salida que devuelve la vista, es decir, el código HTML o el JSON de la respuesta.

Los métodos que tenemos disponibles para hacer el debug de los métodos de prueba, sin necesidad de abrir el navegador y ver las cosas "manualmente", son los siguientes:

Hasta aquí la introducción a las pruebas en Laravel. En los siguientes artículos vamos a explorar muchas otras formas de testear funcionalidades en Laravel utilizando todos los métodos que ofrece para comprobar las solicitudes HTTP, así que tendremos ocasión de explicar los pasos para hacer tests funcionales con mucho más detalle.

Miguel Angel Alvarez

Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...

Manual