Saltar al contenido

TDD, o Test-Driven Development, es una metodología de desarrollo en la que se escriben las pruebas antes de desarrollar el código. En Laravel, esto significa que primero creamos tests para definir cómo debería comportarse nuestra aplicación, y luego escribimos el código necesario para pasar esos tests. Este enfoque ayuda a mejorar la calidad del código y a reducir errores.

En este artículo, aplicaremos el enfoque de Test-Driven Development (TDD) en un proyecto Laravel. Verás cómo ir construyendo paso a paso una pequeña funcionalidad para gestionar tareas, primero escribiendo los tests y luego implementando la lógica mínima para que dichos tests pasen.

Al final del artículo, te dejo un vídeo donde explico todo lo que se hace y cómo se integra con los archivos y la configuración necesaria a través de un proceso completo.

Preparando el entorno con Sail

Para agilizar el desarrollo, utilizaremos Laravel Sail, una configuración de Docker sencilla para entornos locales. Asegúrate de tener Docker instalado y simplemente ejecuta lo siguiente dentro de tu proyecto:

sail up -d

De esta forma, tendrás el contenedor de Laravel corriendo en tu máquina y podrás ejecutar los comandos de Artisan y PHPUnit a través del alias sail (ej. sail artisan, sail test, etc.).

Estructura del proyecto

Nuestra estructura quedará de la siguiente manera:

├── app/
│   └── Http/
│       └── Controllers/
│           └── TaskController.php
├── routes/
│   └── web.php
└── tests/
    ├── Feature/
    │   └── TaskTest.php
    └── ...

Escribiendo nuestro primer Test (Listar Tareas)

La idea principal de TDD es escribir el test primero y ver cómo falla, para después crear la mínima implementación que lo haga funcionar.

Vamos a crear un test llamado TaskTest.php. Para ello, ejecutamos:

sail artisan make:test TaskTest

Nota: Al no definir ningún flag al crear el test, automáticamente se creará un test de tipo feature. Si utilizas el comando --unit, te creará la carpeta Tests\Unit; para este ejemplo usaremos Tests\Feature, por lo que podemos omitir el flag --unit.

En nuestro ejemplo, el archivo de test se ve así:

Analicemos lo que sucede aquí:

  1. taskList(): Verifica que la ruta tasks.index responda con un código 200 OK.

  2. createTask(): Envía un POST a tasks.store con un JSON que contiene la información de la nueva tarea y espera que la respuesta sea 201 Created.

Ejecutando los tests

Si ejecutamos ahora:

sail test

Es probable que (antes de definir las rutas y el controlador) obtengamos errores como:

  • Route [tasks.index] not defined.

  • Route [tasks.store] not defined.

¡Perfecto! Eso es precisamente lo que queremos en TDD: primero fallamos, luego escribimos el código para hacer que pase.

Definiendo las rutas

Creamos nuestras rutas en routes/web.php:

Guardamos. Si volvemos a ejecutar:

sail test

Ahora la parte del enrutado no debería dar error; sin embargo, probablemente tengamos un test fallando por no haber implementado la respuesta 201 en el método store.

Creando el controlador básico

Continuamos con el TDD mínimo: creamos TaskController.php si no existe:

sail artisan make:controller TaskController

Con este código, las pruebas taskList y createTask() deberían pasar, porque:

  • Responden con un código de estado 200 al listar y 201 al crear una tarea (aunque no la esté guardando realmente en la base de datos todavía).

  • Para la fase inicial de TDD, esto está bien. Después, iremos implementando la lógica real de guardado.

Verificando

Ejecutamos de nuevo:

sail test

Si todo está correcto, deberías ver que ambos tests pasan con éxito:

  • taskList(): Responde con 200.

  • createTask(): Responde con 201.

Añadiendo la lógica real (Paso siguiente en TDD)

Una vez que tenemos el test pasando con la implementación más sencilla, llega el momento de refinar y añadir el guardado real en base de datos, la validación, etc. Pero lo haremos gradualmente, siempre asegurándonos de que los tests siguen pasando o, si agregamos nuevas pruebas, primero las hacemos fallar.

Guardar la tarea en la base de datos

Supón que queremos ya registrar la tarea realmente en una tabla tasks. Creamos la migración:

sail artisan make:model Task -m

Editamos la migración para incluir las columnas que necesitamos:

Migramos:

sail artisan migrate

Y actualizamos nuestro TaskController para guardar la tarea:

En tu test podrías añadir más aserciones para comprobar que el JSON devuelto contiene la información de la tarea y que se ha guardado correctamente en la base de datos, además de validaciones y demás.

Conclusiones

A lo largo de este artículo, has visto un ejemplo práctico de Test-Driven Development en Laravel:

  1. Escribir el test primero: Creamos la prueba para listar y crear tareas.

  2. Ejecutar y ver fallar: Nos indica qué falta por implementar (rutas, controlador, etc.).

  3. Escribir la mínima implementación: Creamos la ruta y el método vacío, retornamos la respuesta mínima para que el test pase.

  4. Refactorizar: Vamos añadiendo lógica real (como guardar en base de datos) y ajustamos las pruebas para validar el comportamiento esperado.

Este ciclo se repite una y otra vez para cada parte de la funcionalidad que quieras desarrollar, asegurándote de que el código siempre esté cubierto por las pruebas y que cada nueva característica se demuestre con un test.

Si te interesa seguir aprendiendo TDD en Laravel, te dejo enlaces a 2 cursos sobre TDD aplicado a Laravel de la plataforma.

A continuación, tienes un vídeo donde vemos paso a paso todo el proceso que hemos cubierto en este artículo:

school Curso completo

Curso Laravel 12
Completo 2026

El único curso 100% actualizado que incluye Laravel 12, Livewire 3, Vue 3, React 19 e Inertia 2. Aprende con proyectos reales y las últimas funcionalidades.

access_time 8 horas de contenido
layers 4 tecnologías en 1
update 100% actualizado
code Proyectos prácticos
Ver Curso Laravel 12 arrow_forward

star Incluido en cualquier suscripción

Rutas de aprendizaje