"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Comprender los objetos simulados en las pruebas PHPUnit

Comprender los objetos simulados en las pruebas PHPUnit

Publicado el 2024-11-03
Navegar:711

Understanding Mock Objects in PHPUnit Testing

Al escribir pruebas unitarias, un desafío clave es garantizar que sus pruebas se centren en el código bajo prueba sin interferencia de sistemas externos o dependencias. Aquí es donde entran en juego los objetos simulados en PHPUnit. Le permiten simular el comportamiento de objetos reales de forma controlada, haciendo que sus pruebas sean más confiables y fáciles de mantener. En este artículo, exploraremos qué son los objetos simulados, por qué son útiles y cómo usarlos de manera efectiva en PHPUnit.

¿Qué son los objetos simulados?

Los objetos simulados son versiones simuladas de objetos reales utilizados en pruebas unitarias. Te permiten:

  • Aislar el código bajo prueba: los objetos simulados simulan el comportamiento de las dependencias, lo que garantiza que los resultados de las pruebas no se vean afectados por la implementación real de esas dependencias.
  • Controlar el comportamiento de dependencia: puede especificar cómo debe comportarse el simulacro cuando se llaman ciertos métodos, lo que le permite probar diferentes escenarios.
  • Verificar interacciones: Los simulacros rastrean las llamadas a métodos y sus parámetros, asegurando que el código bajo prueba interactúe correctamente con sus dependencias.

¿Por qué utilizar objetos simulados?

Los simulacros son particularmente útiles en los siguientes escenarios:

  • Dependencias complejas: si su código depende de sistemas externos como bases de datos, API o servicios de terceros, los objetos simulados simplifican las pruebas al eliminar la necesidad de interactuar con esos sistemas.
  • Pruebas de interacción: los simulacros te permiten verificar que se llamen a métodos específicos con los argumentos correctos, asegurando que tu código se comporte como se espera.
  • Ejecución de pruebas más rápida: las operaciones del mundo real, como consultas de bases de datos o solicitudes de API, pueden ralentizar las pruebas. Burlarse de estas dependencias garantiza una ejecución de pruebas más rápida.

Stubbing versus burla: ¿cuál es la diferencia?

Cuando trabajes con objetos simulados, encontrarás dos términos: stubbing y mocking:

  • Stubbing: Se refiere a definir el comportamiento de los métodos en un objeto simulado, por ejemplo, indicarle a un método que devuelva un valor específico.
  • Burla: Implica establecer expectativas sobre cómo se deben llamar los métodos, por ejemplo, verificar la cantidad de llamadas a métodos y sus parámetros.

Cómo crear y utilizar objetos simulados en PHPUnit

PHPUnit facilita la creación y el uso de objetos simulados con el método createMock(). A continuación se muestran algunos ejemplos que demuestran cómo trabajar eficazmente con objetos simulados.

Ejemplo 1: uso básico de objetos simulados

En este ejemplo, creamos un objeto simulado para una dependencia de clase y especificamos su comportamiento.

use PHPUnit\Framework\TestCase;

class MyTest extends TestCase
{
    public function testMockExample()
    {
        // Create a mock for the SomeClass dependency
        $mock = $this->createMock(SomeClass::class);

        // Specify that when the someMethod method is called, it returns 'mocked value'
        $mock->method('someMethod')
             ->willReturn('mocked value');

        // Pass the mock object to the class under test
        $unitUnderTest = new ClassUnderTest($mock);

        // Perform the action and assert that the result matches the expected value
        $result = $unitUnderTest->performAction();
        $this->assertEquals('expected result', $result);
    }
}

Explicación:

  • createMock(SomeClass::class) crea un objeto simulado para SomeClass.
  • método('someMethod')->willReturn('valor simulado') define el comportamiento del simulacro.
  • El objeto simulado se pasa a la clase que se está probando, lo que garantiza que no se utilice la implementación real de SomeClass.

Ejemplo 2: verificación de llamadas a métodos

A veces, es necesario verificar que se llama a un método con los parámetros correctos. Así es como puedes hacerlo:

public function testMethodCallVerification()
{
    // Create a mock object
    $mock = $this->createMock(SomeClass::class);

    // Expect the someMethod to be called once with 'expected argument'
    $mock->expects($this->once())
         ->method('someMethod')
         ->with($this->equalTo('expected argument'))
         ->willReturn('mocked value');

    // Pass the mock to the class under test
    $unitUnderTest = new ClassUnderTest($mock);

    // Perform an action that calls the mock's method
    $unitUnderTest->performAction();
}

Puntos clave:

  • expects($this->once()) garantiza que algún método se llame exactamente una vez.
  • with($this->equalTo('expected argument')) verifica que el método se llama con el argumento correcto.

Ejemplo: prueba con PaymentProcessor

Para demostrar la aplicación en el mundo real de objetos simulados, tomemos el ejemplo de una clase PaymentProcessor que depende de una interfaz PaymentGateway externa. Queremos probar el método de pago del proceso de PaymentProcessor sin depender de la implementación real de PaymentGateway.

Aquí está la clase PaymentProcessor:

class PaymentProcessor
{
    private $gateway;

    public function __construct(PaymentGateway $gateway)
    {
        $this->gateway = $gateway;
    }

    public function processPayment(float $amount): bool
    {
        return $this->gateway->charge($amount);
    }
}

Ahora podemos crear un simulacro de PaymentGateway para probar el procesoMétodo de pago sin interactuar con la pasarela de pago real.

Probar el procesador de pagos con objetos simulados

use PHPUnit\Framework\TestCase;

class PaymentProcessorTest extends TestCase
{
    public function testProcessPayment()
    {
        // Create a mock object for the PaymentGateway interface
        $gatewayMock = $this->createMock(PaymentGateway::class);

        // Define the expected behavior of the mock
        $gatewayMock->method('charge')
                    ->with(100.0)
                    ->willReturn(true);

        // Inject the mock into the PaymentProcessor
        $paymentProcessor = new PaymentProcessor($gatewayMock);

        // Assert that processPayment returns true
        $this->assertTrue($paymentProcessor->processPayment(100.0));
    }
}

Desglose de la prueba:

  • createMock(PaymentGateway::class) crea un objeto simulado que simula la interfaz PaymentGateway.
  • método('charge')->with(100.0)->willReturn(true) especifica que cuando se llama al método de carga con 100.0 como argumento, debe devolver verdadero.
  • El objeto simulado se pasa a la clase PaymentProcessor, lo que le permite probar el procesoPayment sin depender de una pasarela de pago real.

Verificar interacciones

También puedes verificar que el método de cobro se llame exactamente una vez al procesar un pago:

public function testProcessPaymentCallsCharge()
{
    $gatewayMock = $this->createMock(PaymentGateway::class);

    // Expect the charge method to be called once with the argument 100.0
    $gatewayMock->expects($this->once())
                ->method('charge')
                ->with(100.0)
                ->willReturn(true);

    $paymentProcessor = new PaymentProcessor($gatewayMock);
    $paymentProcessor->processPayment(100.0);
}

En este ejemplo, expects($this->once()) garantiza que el método de carga se llame exactamente una vez. Si el método no se llama, o se llama más de una vez, la prueba fallará.

Ejemplo: prueba con un repositorio

Supongamos que tiene una clase UserService que depende de un UserRepository para recuperar datos del usuario. Para probar UserService de forma aislada, puedes burlarte del UserRepository.

class UserService
{
    private $repository;

    public function __construct(UserRepository $repository)
    {
        $this->repository = $repository;
    }

    public function getUserName($id)
    {
        $user = $this->repository->find($id);
        return $user->name;
    }
}

Para probar esta clase, podemos burlarnos del repositorio:

use PHPUnit\Framework\TestCase;

class UserServiceTest extends TestCase
{
    public function testGetUserName()
    {
        // Create a mock for the UserRepository
        $mockRepo = $this->createMock(UserRepository::class);

        // Define that the find method should return a user object with a predefined name
        $mockRepo->method('find')
                 ->willReturn((object) ['name' => 'John Doe']);

        // Instantiate the UserService with the mock repository
        $service = new UserService($mockRepo);

        // Assert that the getUserName method returns 'John Doe'
        $this->assertEquals('John Doe', $service->getUserName(1));
    }
}

Mejores prácticas para el uso de simulacros

  1. Utilice simulaciones solo cuando sea necesario: las simulaciones son útiles para aislar código, pero el uso excesivo puede dificultar la comprensión de las pruebas. Solo simulacros de dependencias que sean necesarias para la prueba.
  2. Céntrese en el comportamiento, no en la implementación: los simulacros deberían ayudar a probar el comportamiento de su código, no los detalles de implementación específicos de las dependencias.
  3. Evite burlarse de demasiadas dependencias: si una clase requiere muchas dependencias simuladas, podría ser una señal de que la clase tiene demasiadas responsabilidades. Refactorice si es necesario.
  4. Verificar interacciones con moderación: Evite llamadas a métodos de verificación excesiva a menos que sean esenciales para la prueba.

Conclusión

Los objetos simulados son herramientas invaluables para escribir pruebas unitarias en PHPUnit. Le permiten aislar su código de dependencias externas, lo que garantiza que sus pruebas sean más rápidas, confiables y fáciles de mantener. Los objetos simulados también ayudan a verificar las interacciones entre el código bajo prueba y sus dependencias, asegurando que su código se comporte correctamente en varios escenarios

Declaración de liberación Este artículo se reproduce en: https://dev.to/ialaminpro/understanding-mock-objects-in-phpunit-testing-c55?1 Si hay alguna infracción, comuníquese con [email protected] para eliminarla.
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3