Test Driven Development (TDD) en el desarrollo de aplicaciones en Laravel

Video thumbnail

Si ya trabajas con Laravel y has usado pruebas unitarias o de integración con PHPUnit o Pest, tarde o temprano aparece una pregunta inevitable:

¿tiene sentido aplicar TDD o es solo una moda más?

El verdadero valor de Test Driven Development (TDD) no lo entendí hasta que empecé a usar las pruebas como una especificación del sistema, no solo como una verificación final. Y Laravel, por cómo está diseñado, encaja perfectamente con esta forma de trabajar.

En este artículo te explico qué es TDD, cómo funciona en Laravel y, sobre todo, cómo aplicarlo con un ejemplo real, incluso cuando la respuesta no es un JSON sino una vista Blade con paginación y porqué tiene sentido emplear este tipo de técnicas al momento de desarrollar; sobre todo que ya conocemos como usar las Pruebas Unitarias y de Integración con PHPUnit y Pest en Laravel.

¿Qué es TDD y por qué tiene sentido en Laravel?

Como consideración adicional, hablamos sobre una técnica llamada Test Driven Development (TDD) en español, Desarrollo basado en pruebas, también conocido como desarrollo guiado por pruebas, la cual es una práctica de programación en la que se escriben pruebas antes de crear el código de la funcionalidad siguiendo los siguientes aspectos claves:

  1. Al definir primero las pruebas, permite especificar cada funcionalidad antes de escribir el código real, es como una especie de árbol mental, pero con código, de esta forma, se guía el proceso de desarrollo.
  2. Código limpio y robusto: El objetivo es crear un código limpio, robusto y simple. Si las pruebas fallan, se corrigen los errores antes de avanzar.

Test Driven Development (TDD), o desarrollo guiado por pruebas, es una técnica en la que escribes primero las pruebas y después el código de la funcionalidad. No al revés.

La idea clave no es “tener tests”, sino que las pruebas definan el comportamiento del sistema. Antes de escribir una sola línea de lógica, ya sabes:

  • Qué ruta debe existir
  • Qué datos debe devolver
  • Qué formato deben tener
  • Qué pasa si algo falla

Laravel facilita muchísimo este enfoque porque viene preparado desde el primer momento para trabajar con pruebas, tanto a nivel de rutas, controladores, base de datos o vistas.

Cuando ya conoces PHPUnit o Pest, TDD deja de ser algo abstracto y se convierte en una forma muy clara de pensar el diseño antes de programar.

El ciclo de TDD: Red, Green y Refactor

El corazón de TDD es un ciclo muy simple, pero poderoso, que se repite constantemente:

Red: escribir una prueba que falle

Primero escribes una prueba para una funcionalidad que todavía no existe.
El test falla, y eso es exactamente lo que buscamos.

Green: escribir el código mínimo

Después escribes solo el código necesario para que la prueba pase.
No más. No menos.

Refactor: mejorar sin romper nada

Una vez el test está en verde, refactorizas con tranquilidad, sabiendo que si algo se rompe, las pruebas te lo dirán.

Este ciclo cambia por completo la forma de desarrollar. En lugar de “programar y luego probar”, pasas a pensar primero en el resultado esperado.

TDD en Laravel: herramientas y tipos de pruebas

Laravel está diseñado con el testing en mente y ofrece varias herramientas clave:

  • PHPUnit: el framework de pruebas clásico en PHP.
  • Pest: una capa moderna y expresiva sobre PHPUnit.
  • Artisan: comandos como php artisan test o make:test.
  • Entorno de testing aislado: base de datos en memoria, sesiones, cache, etc.

Además, Laravel diferencia claramente entre:

  • Tests Unit: lógica pequeña y aislada.
  • Tests Feature: flujos completos HTTP (rutas, vistas, controladores).

Para TDD en Laravel, la mayoría de los casos reales se trabajan muy bien con tests Feature, porque permiten definir el comportamiento completo de una funcionalidad.

Ejemplo para evaluar el Desarrollo basado en pruebas

Para dar un ejemplo de la importancia de las pruebas en el desarrollo de software y por supuesto, que se aplica al desarrollo en Laravel.

La prueba del listado paginado será similar al realizado en la API Rest, pero, la evaluación de la respuesta al no se un JSON si no, un listado paginado, se emplean métodos específicos para tal fin.

Muchas guías se centran en APIs y respuestas JSON, pero en proyectos reales seguimos trabajando con vistas Blade, HTML y datos paginados. Y eso también se puede definir con TDD.

En una prueba real que implementé, el objetivo era claro:

una ruta que muestre un listado paginado de posts en una vista concreta.

Escribiendo la prueba primero

tests\Feature\Web\BlogTest.php

<?php
namespace Tests\Feature\Web;

use App\Http\Controllers\blog\BlogController;
use App\Models\Post;
use Illuminate\Foundation\Testing\DatabaseMigrations;

use Illuminate\Pagination\LengthAwarePaginator;
use Tests\TestCase;

class BlogTest extends TestCase
{
    use DatabaseMigrations;

    function test_index()
    {
        // $response = $this->get('/blog')
          $this
            ->get(route('blog.index'))
            ->assertStatus(200)
            ->assertViewIs('blog.index')
            ->assertSee('Post List')
            ->assertViewHas('posts', Post::paginate(2));
           $this->assertInstanceOf(LengthAwarePaginator::class,$response->viewData('posts'));
    }
}

Antes de implementar nada, esta prueba ya define todo el comportamiento esperado:

  • Existe una ruta con nombre blog.index
  • Devuelve HTTP 200
  • Renderiza la vista blog.index
  • Contiene el texto “Post List”
  • Envía a la vista una variable posts paginada

Aquí es donde entendí de verdad que la prueba es el diseño.

Aserciones clave en Laravel para TDD

Laravel ofrece aserciones muy potentes que encajan perfecto con TDD:

En esta prueba, veremos varios aspectos interesantes, para variar, mostramos que también podemos emplear una ruta con nombre:

->get(route('blog.index'))

assertViewIs()

Con este método de aserción, verificamos por el nombre de la vista, junto con su ruta:

->assertViewIs('blog.index')

assertViewHas()

Con este método, verificamos por la data suministrada a la vista y su nombre, que en este caso es el de posts, que es el listado paginado para los posts:

->assertViewHas('posts', Post::paginate(2));

Con el siguiente método de aserción, obtenemos la data de la vista:

$response->viewData('posts')

Y verificamos que sea instancia de una clase, al estar empleando el:

Post::paginate(2)

Validando el tipo de datos

Sabemos que es de LengthAwarePaginator:

$this->assertInstanceOf(LengthAwarePaginator::class,$response->viewData('posts'));

Esta prueba, que es nuestra primera prueba real sobre la app en Laravel que devuelve una vista, entiéndase un contenido HTML generado con blade y no algo tan simple o plano como un JSON y con esto, podemos ver métodos de aserción más específicos para garantizar que la data tenga el formato esperado.

Como puedes ver, estas pruebas también sirven para especificar donde y como deben estar compuesta elementos como los datos, vista y ruta, por lo tanto, al especificar una estructura clara, tienen sentido técnicas como la de TDD que en pocas palabras, al momento de desarrollar un nuevo proyecto, primero se inicia con las pruebas y son las pruebas las que especifican que es lo que se debe de implementar a nivel de funcionalidades.

Qué nos aporta TDD en proyectos reales

Después de aplicar TDD de esta forma, los beneficios se vuelven muy evidentes:

  • ✔️ Código más simple
    • Si el test pasa, el código es suficiente. No hay sobreingeniería.
  • ✔️ Mejor diseño
    • Las pruebas te obligan a separar responsabilidades y a pensar en contratos claros.
  • ✔️ Confianza al refactorizar
    • Cambiar código deja de dar miedo porque los tests actúan como red de seguridad.
  • ✔️ Documentación viva
    • Los tests explican cómo debe comportarse la aplicación mejor que cualquier README.

En mi experiencia, cuando empiezas un proyecto directamente con TDD, las decisiones técnicas son mucho más coherentes desde el primer día.

Conclusión

TDD en Laravel no va de escribir más tests, sino de usar las pruebas como guía del desarrollo.
Cuando las pruebas definen rutas, vistas, datos y comportamiento, el desarrollo se vuelve más claro, más seguro y más mantenible.

Si ya conoces PHPUnit o Pest, dar el salto a TDD es más natural de lo que parece. Y como has visto, no solo sirve para APIs, sino también para vistas Blade, paginación y flujos reales de una aplicación web.

El siguiente paso natural es profundizar en validaciones, permisos y roles —por ejemplo usando Spatie Permissions— siempre manteniendo las pruebas como el punto de partida.

El siguiente paso, es conocer como usar Spatie y los permisos en Laravel.

Descubre cómo aplicar TDD en Laravel paso a paso con ejemplos reales, pruebas Feature, PHPUnit, Pest y validación de vistas Blade con paginación.

Acepto recibir anuncios de interes sobre este Blog.

Andrés Cruz

EN In english