Kodokon kodokon.com

Calidad: PHPUnit, PHPStan y los PSR

Equipa tu código como un profesional con pruebas PHPUnit, análisis estático PHPStan de nivel 9 y los estándares PSR.

10 min · 3 preguntas

Abrir esta lección en Kodokon

El código de nivel experto se reconoce menos por sus trucos ingeniosos que por su red de seguridad. Tres pilares complementarios: las pruebas (PHPUnit) que verifican el comportamiento ejecutando el código, el análisis estático (PHPStan) que demuestra propiedades sin ejecutar nada, y los estándares compartidos (PSR) que hacen que el ecosistema sea interoperable. Todo se instala con Composer, como dependencias de desarrollo.

BASH
composer require --dev phpunit/phpunit
composer require --dev phpstan/phpstan
./vendor/bin/phpunit --testdox tests
./vendor/bin/phpstan analyse src --level=9
La opción --testdox muestra las pruebas como una especificación legible.

Desde PHPUnit 10, los metadatos se escriben como atributos PHP: #[DataProvider] reemplaza la anotación de docblock @dataProvider. Un data provider debe ser público y estático, porque PHPUnit lo lee incluso antes de instanciar la clase de prueba. Cada conjunto de datos lleva una clave descriptiva, mostrada tal cual cuando el caso falla - un fallo accents se localiza al instante.

PHP
<?php
declare(strict_types=1);

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

final class SlugifierTest extends TestCase
{
    #[DataProvider('provideTitles')]
    public function testSlugify(
        string $input,
        string $expected
    ): void {
        $this->assertSame($expected, slugify($input));
    }

    public static function provideTitles(): array
    {
        return [
            'simple' => [
                'Hello World', 'hello-world'
            ],
            'accents' => [
                'Déjà vu', 'deja-vu'
            ],
        ];
    }
}
Prueba una función slugify definida en otra parte de tu proyecto.

PHPStan gradúa su rigor por niveles, de 0 a 9 (existe un nivel 10 en las versiones recientes): en el nivel 9, cualquier valor mixed se vuelve inutilizable sin antes acotarlo mediante comprobaciones explícitas. Su verdadero poder proviene de los tipos de docblock que el motor de PHP ignora pero que el analizador verifica: formas de array array{email: string}, listas list<int>, genéricos. Documentan y demuestran al mismo tiempo.

PHP
<?php
declare(strict_types=1);

function findEmail(?array $user): string
{
    return $user['email'];
}

/**
 * @param array{email: string}|null $user
 */
function findEmailSafe(?array $user): string
{
    return $user === null
        ? 'n/a'
        : $user['email'];
}
Nivel 9: la primera función se rechaza (posible acceso sobre null, clave y tipo desconocidos), la segunda pasa.

Los PSR (PHP Standards Recommendations) son las convenciones de PHP-FIG. PSR-1 y PSR-12 estandarizan el estilo: indentación, llaves, orden de las importaciones - aplicados automáticamente por php-cs-fixer o phpcs. PSR-4 define la carga automática: un prefijo de espacio de nombres se asocia a un directorio, lo que permite a Composer localizar cualquier clase sin un require manual. Por último, los PSR de interfaz - PSR-3 para los loggers, PSR-7 para los mensajes HTTP - te permiten cambiar una biblioteca por otra sin tocar el código que la consume.

Prueba de conocimientos

Comprueba que has retenido los puntos clave de esta lección.

  1. Desde PHPUnit 10, ¿qué restricción se aplica a un método data provider referenciado por #[DataProvider]?
    • Debe ser público y estático, porque PHPUnit lo lee antes de instanciar la clase de prueba
    • Debe devolver un generador, nunca un array
    • Debe tener el mismo nombre que la prueba, con el sufijo Provider
  2. ¿Qué impone el nivel 9 de PHPStan?
    • Prohibir los docblocks en favor de únicamente los tipos nativos
    • Un tratamiento estricto de mixed: no puedes usar un valor de ese tipo sin acotarlo primero
    • Una cobertura de pruebas del 100% sobre el código analizado
  3. ¿Qué define el estándar PSR-4?
    • El formato de los mensajes de registro de la aplicación
    • La asociación entre un prefijo de espacio de nombres y un directorio, usada por el autoloader de Composer
    • El estilo de indentación y la colocación de las llaves