PHPUnit: Dominando Covers para Mejor Navegación y Cobertura en Pruebas
Probar controladores web en PHP suele generar líos de navegación entre las pruebas y el código de la app. Vamos a ver cómo las anotaciones y atributos @covers de PHPUnit resuelven esto, agilizando el desarrollo sin sacrificar la cobertura.
El Problema de Navegación entre Pruebas y Código
Al probar controladores mediante peticiones HTTP, no puedes invocar métodos directamente, lo que complica saltar de la prueba al código. Por ejemplo, una prueba para el endpoint /hello no hace referencia directa a GreetingController::hello(). Esto obliga a búsquedas manuales en el IDE, ralentizándote.
Aquí un ejemplo de controlador y prueba:
final class GreetingController extends AbstractController
{
#[Route('/hello')]
public function hello(): Response
{
return new Response($this->generate("¡Hola!"));
}
}
class GreetingControllerTest extends WebTestCase
{
public function test_hello(): void
{
$content = get('/hello');
assert_starts_with('¡Hola!', $content);
}
}
Tres Formas de Vincular Pruebas con Código
Los desarrolladores usan varios trucos para mejorar la navegación.
1. Sintaxis Nativa con Reflexión
Cambia rutas de strings por llamadas a métodos vía reflexión, como request(GreetingController::hello(...)). Desventaja: Las pruebas pierden independencia del código de la app, pudiendo romperse con cambios.
2. Anotación @see
Usa PHPDoc para enlazar:
/**
* @see \App\Controller\GreetingController::hello()
*/
public function test_hello(): void { ... }
Ventajas: Soporte del IDE. Desventajas: Requiere rutas completas de clases, etiqueta genérica sin manejo especial.
3. Covers Especializados de PHPUnit
Anotaciones o atributos diseñados para cobertura y navegación.
Usando Anotaciones Covers en PHPUnit
Antes de PHPUnit 12, @coversDefaultClass y @covers mandaban:
/**
* @coversDefaultClass \App\Controller\GreetingController
*/
class GreetingControllerTest extends WebTestCase
{
/**
* @covers ::hello
*/
public function test_hello(): void { ... }
}
Ventajas:
- Soporte excelente en PhpStorm: autocompletado, navegación, detección de uso.
- Plantillas personalizadas para generar pruebas.
Desventajas de plantillas en PhpStorm:
- Limitadas a dos plantillas de PHPUnit.
- Nombres confusos.
- Fallos ocasionales en el generador.
Pasando a Atributos Covers en PHPUnit 12+
PHPUnit 12 eliminó las anotaciones por atributos, pero con trampa: solo aplican a clases, no a métodos.
Uso Básico:
#[CoversClass('App\Controller\GreetingController')]
class GreetingControllerTest extends WebTestCase
{
public function test_hello(): void { ... }
}
Esto perjudica la precisión de navegación sin especificar métodos.
Especificando Métodos con CoversMethod:
#[CoversMethod('App\Controller\GreetingController', 'hello')]
#[CoversMethod('App\Controller\GreetingController', 'bye')]
class GreetingControllerTest extends WebTestCase
{
public function test_hello(): void { ... }
public function test_bye(): void { ... }
}
Desventaja: Atributos acumulados en pruebas con múltiples métodos.
Estrategia Ideal: Una Clase de Prueba por Método
Clases de prueba dedicadas por método simplifican todo:
#[CoversMethod('App\Controller\GreetingController', 'hello')]
class HelloTest extends WebTestCase
{
public function test_hello(): void { ... }
public function test_hello2(): void { ... }
}
Beneficios:
- Menos carga mental.
- No hace falta covers por método.
- Promueve diseño limpio: un controlador, un endpoint.
Las plantillas de atributos funcionan como anotaciones, pero requieren ajustes manuales en CoversMethod tras generar.
Integración con IDE y Mejoras Futuras
PhpStorm ya maneja saltos de prueba a clase con Ctrl+Shift+T. Espera navegación a nivel de método pronto.
Lecciones Clave
- Usa covers para potenciar navegación, pero ten en cuenta el impacto en cobertura.
- Atributos de PHPUnit 12+ son solo para clases, así que adapta tus pruebas.
- Ideal: Una clase de prueba por método para mantenimiento fácil.
- Configura plantillas en IDE para covers automáticos.
- Sigue las actualizaciones de PhpStorm para mejor soporte de atributos.
Límites de Cobertura: El Mayor Defecto de Covers
La función principal de Covers es acotar la cobertura a clases/métodos. CoversClass excluye código externo de informes —ideal para proyectos estrictos, pero la mayoría solo quiere detectar huecos.
Soluciones para desactivar:
- Truco Excesivo: Scripts que eliminan etiquetas antes de ejecutar pruebas.
- Solución Práctica: Parchea phpunit/php-code-coverage vía Composer.
Elige según necesidades del proyecto y preferencias del equipo.
— Editorial Team
Aún no hay comentarios.