Introducción: ¿Por qué son necesarias las pruebas en PHP?
\n\nDesarrollar en PHP sin pruebas es como construir una casa sin cimientos. Tarde o temprano la aplicación fallará y los usuarios verán la «pantalla blanca de la muerte». Las pruebas de código no son un lujo, sino una necesidad para cualquier proyecto serio. Permiten:
\n- \n
- Detectar errores en etapas tempranas — antes de que el código llegue a producción. \n
- Simplificar la refactorización — puedes cambiar la arquitectura con confianza, sabiendo que las pruebas detectarán regresiones. \n
- Crear código autodocumentado — las pruebas sirven como documentación viva de cómo debe funcionar el sistema. \n
- Aumentar la confianza del equipo — cada nuevo commit pasa una verificación automática. \n
En PHP existen tres niveles principales de pruebas: Unit (unitarias), Integration (de integración) y E2E (de extremo a extremo). En este artículo analizaremos cada uno con ejemplos prácticos, desde simples assert hasta pruebas completas en navegador.
\n\n1. Pruebas unitarias: probando los «ladrillos» de la aplicación
\n\n¿Qué es una prueba unitaria?
\nUna prueba unitaria verifica el funcionamiento de un pequeño bloque de código — generalmente un método o función — en completo aislamiento de dependencias externas (base de datos, API, sistema de archivos).
\n\nHerramientas para PHP
\n- \n
- PHPUnit — el estándar de facto. Se utiliza en Laravel, Symfony y muchos CMS. \n
- Pest — una alternativa moderna con una sintaxis más concisa, construida sobre PHPUnit. \n
Ejemplo: probando una calculadora de descuentos
\nSupongamos que tenemos una clase simple DiscountCalculator:
<?php\n\nclass DiscountCalculator\n{\n public function calculate(float $price, float $percent): float\n {\n if ($percent < 0 || $percent > 100) {\n throw new \\InvalidArgumentException('El porcentaje debe estar entre 0 y 100');\n }\n return $price - ($price * $percent / 100);\n }\n}\n\n\nAhora escribamos una prueba unitaria con PHPUnit:
\n\n<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\nclass DiscountCalculatorTest extends TestCase\n{\n private DiscountCalculator $calculator;\n\n protected function setUp(): void\n {\n $this->calculator = new DiscountCalculator();\n }\n\n public function testCalculateWithZeroPercent(): void\n {\n $result = $this->calculator->calculate(100, 0);\n $this->assertEquals(100, $result);\n }\n\n public function testCalculateWithFiftyPercent(): void\n {\n $result = $this->calculator->calculate(200, 50);\n $this->assertEquals(100, $result);\n }\n\n public function testCalculateThrowsExceptionOnInvalidPercent(): void\n {\n $this->expectException(\\InvalidArgumentException::class);\n $this->calculator->calculate(100, 150);\n }\n}\n\n\nPuntos clave:
\n- \n
- Cada método prueba solo un escenario. \n
- No accedemos a la BD ni a servicios externos. \n
- Usamos
setUp()para inicializar objetos comunes. \n
Mejores prácticas de pruebas unitarias
\n- \n
- Sigue el principio FIRST: Fast, Isolated, Repeatable, Self-validating, Timely. \n
- Nombra las pruebas como «test_acción_resultadoEsperado». \n
- Evita la lógica en las pruebas — nada de if/switch/bucles. \n
2. Pruebas de integración: verificando la interacción de componentes
\n\n¿Cuál es la diferencia con Unit?
\nLas pruebas de integración verifican cómo diferentes partes del sistema funcionan juntas. Por ejemplo, cómo tu servicio interactúa con una base de datos real, caché o API externa.
\n\nEjemplo: probando un repositorio con BD (Laravel + SQLite)
\nSupongamos que tenemos un <