El flujo de trabajo del desarrollo guiado por pruebas
Guíe el diseño con el ciclo rojo-verde-refactorización
El flujo de trabajo del desarrollo guiado por pruebas es una lección gratuita de PHP Academy en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de PHP Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de PHP Academy incluye 4 lecciones en total.
TDD es una herramienta de diseño
El desarrollo guiado por pruebas suele presentarse como una técnica de pruebas, pero su verdadero valor es la presión de diseño. Al escribir primero la prueba, se ve obligado a definir la interfaz pública de un objeto, sus dependencias y su contrato antes de escribir cualquier implementación. Las pruebas son un subproducto; un diseño mejor es el producto.
Rojo, verde, refactorización
El ciclo tiene tres fases estrictas:
- Rojo — escriba una prueba fallida para el siguiente comportamiento mínimo. Debe fallar por el motivo correcto.
- Verde — escriba el mínimo código necesario para que pase, aunque no sea elegante.
- Refactorización — mejore la implementación y las pruebas sin dejar de estar en verde.
La disciplina es importante: nunca omita Rojo, porque podría estar probando nada, y nunca implemente de más durante Verde.
Rojo: escriba primero la prueba fallida
Construiremos un PriceCalculator que aplica un descuento porcentual. Comience con una prueba para un comportamiento que aún no existe. Al ejecutarla debería fallar porque la clase no está definida: ese es un Rojo válido.
<?php
use PHPUnit\Framework\TestCase;
final class PriceCalculatorTest extends TestCase
{
public function test_applies_a_percentage_discount(): void
{
$calc = new PriceCalculator();
// 100 with 10% off -> 90.0
self::assertSame(90.0, $calc->withDiscount(100.0, 10));
}
}
Verde: el código mínimo para pasar
Ahora escriba la implementación más pequeña que haga que la prueba pase. Resista la tentación de añadir reglas de redondeo, validación o gestión de divisas: todavía no hay ninguna prueba fallida que las exija.
<?php
final class PriceCalculator
{
public function withDiscount(float $amount, float $percent): float
{
return $amount - ($amount * $percent / 100);
}
}
$calc = new PriceCalculator();
var_dump($calc->withDiscount(100.0, 10)); // float(90)
Use la triangulación para impulsar la generalidad
Una sola prueba puede satisfacerse codificando un valor fijo. La triangulación — añadir un segundo ejemplo diferente — obliga a la implementación a generalizar. Añada un caso que la solución trivial no pueda falsear y haga aflorar la fórmula real.
<?php
use PHPUnit\Framework\TestCase;
final class PriceCalculatorTest extends TestCase
{
public function test_zero_percent_is_unchanged(): void
{
self::assertSame(50.0, (new PriceCalculator())->withDiscount(50.0, 0));
}
public function test_full_discount_is_free(): void
{
self::assertSame(0.0, (new PriceCalculator())->withDiscount(50.0, 100));
}
}
Convierta los casos límite en especificaciones
En TDD, un requisito nuevo se traduce en una nueva prueba fallida. Suponga que los descuentos negativos no son válidos. Escriba primero la prueba que espera una excepción; falla porque todavía no se lanza ninguna. La prueba documenta el contrato.
<?php
use PHPUnit\Framework\TestCase;
final class PriceCalculatorValidationTest extends TestCase
{
public function test_rejects_negative_discount(): void
{
$this->expectException(\InvalidArgumentException::class);
(new PriceCalculator())->withDiscount(100.0, -5);
}
}
Vuelva a Verde y después refactorice
Añada la validación para que pase la nueva prueba y después refactorice con confianza: las pruebas existentes detectarán las regresiones. Observe que también introdujimos el redondeo solo cuando una prueba o un requisito conocido lo justificó.
<?php
final class PriceCalculator
{
public function withDiscount(float $amount, float $percent): float
{
if ($percent < 0 || $percent > 100) {
throw new \InvalidArgumentException('percent must be 0..100');
}
return round($amount - ($amount * $percent / 100), 2);
}
}
var_dump((new PriceCalculator())->withDiscount(19.99, 15)); // float(16.99)
Los proveedores de datos condensan los ejemplos
Una vez que un comportamiento es estable, reúna muchos pares de ejemplos en una sola prueba parametrizada con un proveedor de datos. Esto mantiene rápido el ciclo de Rojo/Verde y hace que la tabla de intenciones sea fácil de leer.
<?php
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\DataProvider;
final class DiscountTableTest extends TestCase
{
#[DataProvider('cases')]
public function test_discounts(float $amount, float $pct, float $expected): void
{
self::assertSame($expected, (new PriceCalculator())->withDiscount($amount, $pct));
}
public static function cases(): array
{
return [
'no discount' => [100.0, 0, 100.0],
'ten percent' => [100.0, 10, 90.0],
'free' => [100.0, 100, 0.0],
];
}
}
Mantenga rápido el ciclo
TDD solo funciona si el ciclo de comentarios dura segundos, no minutos. Algunas medidas prácticas:
- Ejecute un solo archivo o aplique un filtro:
phpunit --filter test_full_discount_is_free. - Use
--testdoxpara leer las pruebas como una especificación de comportamiento. - Mantenga las pruebas unitarias libres de E/S: nada de base de datos, red o sistema de archivos en el ciclo interno.
vendor/bin/phpunit --testdox --filter PriceCalculatorRefactorice también las pruebas
La fase de refactorización abarca el conjunto de pruebas, no solo el código de producción. Elimine la duplicación con auxiliares y setUp(), ponga a las pruebas nombres basados en el comportamiento y elimine las que ya no comprueben nada significativo. Las pruebas son código que mantendrá para siempre: trátelas con el mismo cuidado.
<?php
use PHPUnit\Framework\TestCase;
final class PriceCalculatorTest extends TestCase
{
private PriceCalculator $calc;
protected function setUp(): void
{
$this->calc = new PriceCalculator(); // shared setup, no duplication
}
public function test_ten_percent(): void
{
self::assertSame(90.0, $this->calc->withDiscount(100.0, 10));
}
}
TDD da forma a las dependencias
Como escribe primero la prueba, las dependencias incómodas se vuelven evidentes de inmediato. Si resulta difícil instanciar una clase en una prueba, eso es información sobre el diseño: inyecte colaboradores en lugar de crearlos con new en su interior. El calculador de descuentos de abajo se vuelve comprobable al aceptar su política de redondeo, en vez de codificarla directamente; TDD obligó a introducir ese punto de extensión.
<?php
interface RoundingPolicy { public function round(float $v): float; }
final class PriceCalculator
{
public function __construct(private RoundingPolicy $rounding) {}
public function withDiscount(float $amount, float $percent): float
{
return $this->rounding->round($amount - ($amount * $percent / 100));
}
}
// Tests inject a deterministic RoundingPolicy; no hidden global behavior.
Comprobación rápida
¿Cuál es el objetivo del paso Rojo?
Resumen
Ha practicado TDD como disciplina de diseño:
- Rojo-Verde-Refactorización: prueba fallida, código mínimo y después limpieza sin salir de Verde.
- La triangulación obliga a generalizar; los requisitos nuevos llegan como nuevas pruebas fallidas.
- Los proveedores de datos condensan los casos estables;
--filter/--testdoxmantienen rápido el ciclo. - Refactorice también las pruebas: son código de larga duración.
A continuación: dobles de prueba flexibles con Mockery.
Preguntas frecuentes
¿La lección «El flujo de trabajo del desarrollo guiado por pruebas» es gratis?
Sí — el texto completo de «El flujo de trabajo del desarrollo guiado por pruebas» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de PHP Academy, actualiza a CoddyKit PRO. El curso de PHP Academy incluye 4 lecciones en total.
¿Qué aprenderé en «El flujo de trabajo del desarrollo guiado por pruebas»?
Guíe el diseño con el ciclo rojo-verde-refactorización Practicas PHP Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar PHP Academy?
No se requiere experiencia previa. PHP Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.
¿Cuánto tiempo toma la lección «El flujo de trabajo del desarrollo guiado por pruebas»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de PHP Academy?
Sí. Cada lección de PHP Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- El flujo de trabajo del desarrollo guiado por pruebas
- Mocks y stubs con Mockery
- Pruebas de integración y funcionales
- Pruebas de mutación con Infection