Cómo trabaja Laravel con el tema de las excepciones y cómo organizar el código de pruebas para testear casos excepcionales.
A la hora de testear nuestro software también debemos tener en cuenta las excepciones. A lo largo de este y los próximos artículos vamos a abordar este asunto. Un primer paso es entender qué pasa cuando surgen excepciones en nuestras aplicaciones Laravel, algo que necesitaremos conocer para poder testear bien el software de nuestras aplicaciones.
Cómo trabaja Laravel con las excepciones
Como en cualquier desarrollo de software, los programadores debemos hacer el tratamiento de las excepciones que se puedan producir. Sin embargo, aunque no lo hagamos de manera explícita, el propio framework por debajo va a hacer un tratamiento de las excepciones por nosotros.
Por ese motivo, antes de ponernos a testear las excepciones merece la pena saber cómo Laravel las va a tratar de manera genérica. Incluso debemos entender qué diferencias existen en el tratamiento de las excepciones entre el modo desarrollo con debug activado y el modo de producción, donde no tenemos está la depuración activa.
Cómo trata Laravel las excepciones en modo debug
La manera en la que Laravel va a tratar una excepción en cuando estás en modo debug (el modo de desarrollo y el modo de testing) es levantar una completa información del error, detallando el problema que se ha encontrado al ejecutar la solicitud requerida o al ejecutar la prueba.
Este detalle del error lo habremos visto infinidad de veces al desarrollar una página y acceder a ella en el navegador.
Por otra parte, si estamos en pruebas, esa página de error no se ve detalladamente, sino que en la consola nos muestra de manera resumida las claves de los motivos por los que los asserts no han funcionado, lo que puede traer consigo algunos resúmenes de los errores encontrados, pero no el detalle completo.
Ya lo comentamos anteriormente, pero si deseamos ver el detalle completo de un error tratado tenemos que ejecutar un método dump(), que se ejecuta sobre el objeto "response" y que nos detalla información completa para la depuración.
$response->dump();
Cómo trata Laravel las excepciones cuando no estás en modo debug
Si no tienes el debug activado, que es lo normal en entornos de producción, entonces Laravel también captura la excepción en algún momento. Sin embargo, lo que hará el framework es mostrarte una página de error específica. Puede ser la página de error predeterminada que viene cuando instalas la aplicación o bien una página personalizada que podemos haber configurado nuestro proyecto.
Como sabes, Laravel tiene la posibilidad de personalizar las páginas de error por medio de la configuración de una vista. La página que se muestra cuando se producen errores de sintaxis y excepciones no tratadas se configura en la vista que está en la ruta
resources/views/errors/500.blade.php.
En todo caso, Laravel sí que tratará la excepción en algún momento, por lo que no llegará a "estallar" la aplicación de una manera típica a como estallaría cuando estás en PHP sin frameworks.
Por tanto, por lo que respecta al tratamiento de las excepciones es indiferente si estás en modo desarrollo o test (con el debug activado), como en modo producción (con el debug desactivado): Laravel siempre las va a tratar. La única diferencia es cómo mostrará el error.
Si tienes la depuración activada lo muestra en la propia página web pero cuando estamos en modo producción no podremos ver el detalle del error. En este caso, como sabemos, se va al archivo de logs de la aplicación.
Cómo verificar que una excepción fue reportada en Laravel
En Laravel, cuando decimos que una excepción fue "reportada" nos referimos a que llegó al manejador de excepciones central de Laravel para ser tratada de algún modo, por ejemplo enviada al archivo de log o quizás enviada a algún servicio de monitoreo de logs como Sentry.
Hay veces que queremos verificar que justamente cierta situación produce una excepción que debe haber sido reportada. Para ello tenemos que hacer dos pasos.
Primero debemos hacer un fake del sistema de excepciones de Laravel. Eso se consigue llamando al método fake() del Facade Exceptions.
Exceptions::fake();
Recuerda hacer el use
Illuminate\Support\Facades\Exceptions.
Luego puedes verificar que tu excepción haya sido reportada con el método assertReported() del facade Exceptions, enviando el nombre de la excepción que deseas comprobar que ha sido reportada.
Exceptions::assertReported(InvalidArgumentException::class);
También podríamos verificar el caso contrario, que una excepción no ha sido reportada:
Exceptions::assertNotReported(InvalidArgumentException::class);
O que ninguna excepción ha sido reportada:
Exceptions::assertNothingReported();
Desactivar totalmente el manejo de excepciones de Laravel
Si deseas desactivar totalmente el sistema de manejo de excepciones que viene incorporado con Laravel puedes invocar el método withoutExceptionHandling() que hay disponible en las clases de test de "Features" de Laravel que heredan de Tests\TestCase.
$response = $this->withoutExceptionHandling()->get('/create-custom-exception');
Esto nos puede venir bien cuando queremos que se muestre el error completo, en el momento que se produce en el código que estamos probando, en lugar del resumen que te aporta el sistema de manejo de excepciones de Laravel.
Usando esta funcionalidad, al producirse el error se parará abruptamente la ejecución de todos los test y se mostrará en la consola lo que está pasando. A veces es útil cuando no sabes por dónde viene el error que se detecta en las pruebas y quieres que sea mostrado inmediatamente en la consola para poder debuggear tu código de aplicación.
Si has desactivado el sistema global de manejo de excepciones de Laravel aún podrías usar aserciones de expectException de PHPUnit, como puedes ver en el siguiente código.
#[Test]
public function without_exception_handling(): void
{
$this->expectException(InvalidArgumentException::class);
$this->withoutExceptionHandling()->get('/panel/create-custom');
}
Solo recuerda que el
expectExceptiondebe definirse antes de ejecutar el código que esperamos que devuelva cierta excepción, para que la prueba no se detenga y se pueda dar como válida. Esto es algo más de PHPUnit que del propio Laravel.
Método assertThrows de Laravel
Otra cosa que que Laravel nos ofrece como extra es el método assertThrows que nos permite definir código a ejecutar dentro de una closure, de modo que podamos ver si al ejecutarla se produce cierta excepción.
El método assertThrows() recibe dos argumentos. El primero es la closure que debemos ejecutar y el segundo la excepción que se espera recibir.
#[Test]
public function using_assert_throws(): void
{
$this->assertThrows(
fn () => (new ActivityTimeStamp("10", "2"))->execute(),
InvalidArgumentException::class
);
}
De este modo podemos ver qué pasaría al ejecutar código que debería levantar excepciones, sin necesidad de que ese código esté en algún lugar de nuestra aplicación.
También existe la posibilidad de enviar como segundo argumento una segunda closure, en cuyo caso lo que permitirá es recibir la excepción como parámetro para examinarla con más detalle. Consulta la documentación de Laravel para ver ese caso más avanzado.
Estas son las cosas generales que debes conocer sobre excepciones en Laravel y cómo organizar con este conocimiento tu código de pruebas. En el siguiente artículo vamos a explicar algunos detalles muy prácticos de cosas sencillas que puedes hacer en Laravel para probar diversos casos excepcionales con las solicitudes HTTP.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...