> Manuales > Manual de PHP

Cómo resumir las tareas de creación de objetos en tus test unitarios de PHP con PHPUnit, con setUp() y tearDown(), de manera que estén bien organizados y mantenibles.

Organiza los casos de test con PHPUnit con setUp y tearDown

PHPUnit es el framework de testing más popular para PHP. En este artículo podrás mejorar tus habilidades y conocer los aspectos más importantes para desarrollar tus propias pruebas de una manera más organizada, resumiendo las tareas de inicialización y restauración del sistema antes y después de cada método de test.

Referencias para instalación de PHPUint y primeros test

Este es un tema que ya hemos tocado en DesarrolloWeb.com, por lo que no vamos a repetirnos. Si nunca has desarrollado los test unitarios te recomendamos antes acceder a estas dos categorías:

En este artículo vamos a ir unos pasitos por delante para aprender cosas nuevas que, siendo todavía básicas, no hemos tocado en las referencias anteriores.

Cómo resumir la preparación de los test con el setUp()

Existen un par de métodos de test que debes de conocer, ya que te permiten organizar el código de los test, uno es el setUp() y otro el tearDown(). Vamos a comenzar con el más importante de los dos, el método setUp().

El método setUp() se ejecuta automáticamente antes de la ejecución de cada método de prueba. Esto garantiza que todas las pruebas comiencen con un estado inicial requerido por los métodos de prueba de una clase de test.

Desde PHPUnit 8, su firma debe ser:

protected function setUp(): void

En el setup podrías hacer cosas como crear un objeto que puedas necesitar para todas las pruebas.

Algunos consejos a la hora de usar setUp().

Un ejemplo podría ser este:

protected function setUp(): void {
    $this->calculadora = new CalculadoraDeImpuestos();
}

En este caso estamos haciendo una instancia de una clase CalculadoraDeImpuestos que se supone que usaremos en cada uno de los métodos de test. Como el método setUp no devuelve nada, si quieres almacenar esa instancia, de modo que esté disponible para todos los métodos de test, entonces tendrías que usar una propiedad de la clase de test.

Usando métodos auxiliares como setUp()

Puede ocurrir que tu setup no sea igual para todos los casos de prueba. Por ejemplo, tu calculadora de impuestos podría requerir configuraciones distintas en algunos casos. Entonces en el método setUp no te recomendamos crear todos los objetos calculadora con las configuraciones distintas. En lugar de eso lo ideal sería apoyarse en métodos auxiliares.

Por supuesto, también podrías crear tu calculadora con la configuración que requiera cada test en el propio método que estás testeando, pero eso te impediría disfrutar de una misma configuración para varios métodos de test, así como resumir las tareas de configuración si éstas son más complejas que crear un simplo objeto

Incluso, si no es muy costoso crear los objetos en términos de procesamiento, no sería una mala idea crear una configuración por defecto en el método setUp() y luego algunos métodos auxiliares para configuraciones específicas.

<?php

use PHPUnit\Framework\TestCase;

class CalculadoraDeImpuestosTest extends TestCase
{
    private CalculadoraDeImpuestos $calculadora;

    protected function setUp(): void
    {
        // Aquí solo configuramos la calculadora por defecto
        $this->calculadora = $this->crearCalculadoraPorDefecto();
    }

    private function crearCalculadoraPorDefecto(): CalculadoraDeImpuestos
    {
        $config = new Config(['tasa' => 0.21, 'pais' => 'ES']);
        return new CalculadoraDeImpuestos($config);
    }

    private function crearCalculadoraReducida(): CalculadoraDeImpuestos
    {
        $config = new Config(['tasa' => 0.10, 'pais' => 'ES']);
        return new CalculadoraDeImpuestos($config);
    }

    public function testCalculadoraConTasaPorDefecto(): void
    {
        $resultado = $this->calculadora->calcular(100);
        $this->assertEquals(21, $resultado);
    }

    public function testCalculadoraConTasaReducida(): void
    {
        $calculadoraReducida = $this->crearCalculadoraReducida();
        $resultado = $calculadoraReducida->calcular(100);
        $this->assertEquals(10, $resultado);
    }
}

Método tearDown()

Por otro lado, el método tearDown() se encarga de ejecutar acciones después de que finaliza cada uno de los métodos de prueba.

No es tan habitual su uso como el setUp() pero merece la pena que lo conozcas. La firma de este método es la siguiente.

protected function tearDown(): void

En el tearDown() podrías hacer cosas como finalizar los objetos o restaurar estados en archivos o bases de datos.

Por ejemplo, podrías usar este método para cerrar la apertura de un fichero o una conexión con la base de datos. Veamos un ejemplo básico.

<?php

use PHPUnit\Framework\TestCase;

class ReporteFinancieroTest extends TestCase
{
    private PDO $conexion;

    protected function setUp(): void
    {
        // Simulamos una conexión a la base de datos (por ejemplo, SQLite en memoria)
        $this->conexion = new PDO('sqlite::memory:');
        $this->conexion->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        
        $this->conexion->exec('CREATE TABLE reportes (id INTEGER PRIMARY KEY, total DECIMAL)');
    }

    // Aquí podría tener sentido cerrar la conexión a la base de datos después de cada prueba con el método tearDown siguiente:
    protected function tearDown(): void
    {
        $this->conexion = null;
    }

    public function testInsertarReporte(): void
    {
        $stmt = $this->conexion->prepare('INSERT INTO reportes (total) VALUES (:total)');
        $stmt->execute(['total' => 100.50]);

        $resultado = $this->conexion->query('SELECT COUNT(*) FROM reportes')->fetchColumn();

        $this->assertEquals(1, $resultado);
    }

    public function testTotalReporte(): void
    {
        $this->conexion->exec("INSERT INTO reportes (total) VALUES (200.75)");
        $resultado = $this->conexion->query('SELECT total FROM reportes WHERE id = 1')->fetchColumn();

        $this->assertEquals(200.75, $resultado);
    }
}

Otro caso frecuente sería restablecer las configuraciones globales que puedan haberse realizado en los métodos de test.

<?php

use PHPUnit\Framework\TestCase;

class ConfiguracionGlobalTest extends TestCase
{
    private string $originalTimezone;

    protected function setUp(): void
    {
        $this->originalTimezone = date_default_timezone_get();
    }

    // En este caso restablecemos la configuración original después de cada test, lo que tendría sentido si en algún método o métodos de test la hemos cambiado
    protected function tearDown(): void
    {
        date_default_timezone_set($this->originalTimezone);
    }

    public function testCambioZonaHoraria(): void
    {
        date_default_timezone_set('America/New_York');

        $this->assertEquals('America/New_York', date_default_timezone_get());
    }
}

Esperamos que estos ejemplos te hayan resultado de utilidad para entender los métodos setUp() y tearDown() y usarlos de manera correcta en tus clases de test.

Miguel Angel Alvarez

Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...

Manual