Estamos actualizando el sitio. Durante unos días es posible que veas fallos de diseño o de traducción. La documentación sigue disponible: si una página se ve mal, recárgala más tarde.

Inicio Laravel 10.x Simulación (Mocking)

Simulación (Mocking)

10.x 7 de mar. de 2026

#Introducción

Al probar aplicaciones Laravel, puede que desee "simular" ciertos aspectos de su aplicación para que no se ejecuten realmente durante una prueba determinada. Por ejemplo, al probar un controlador que despacha un evento, puede que quiera simular los listeners del evento para que no se ejecuten durante la prueba. Esto le permite probar solo la respuesta HTTP del controlador sin preocuparse por la ejecución de los listeners, ya que estos pueden probarse en su propio caso de prueba.

Laravel proporciona métodos útiles para simular eventos, jobs y otros facades de forma nativa. Estos helpers ofrecen principalmente una capa de conveniencia sobre Mockery para que no tenga que hacer llamadas complejas a métodos de Mockery manualmente.

#Simulación de objetos

Al simular un objeto que será inyectado en su aplicación mediante el service container de Laravel, deberá enlazar su instancia simulada en el contenedor como un binding de tipo instance. Esto indicará al contenedor que use su instancia simulada del objeto en lugar de construir el objeto por sí mismo:

use App\Service;
use Mockery;
use Mockery\MockInterface;

public function test_something_can_be_mocked(): void
{
    $this->instance(
        Service::class,
        Mockery::mock(Service::class, function (MockInterface $mock) {
            $mock->shouldReceive('process')->once();
        })
    );
}

Para hacerlo más conveniente, puede usar el método mock que proporciona la clase base de test de Laravel. Por ejemplo, el siguiente ejemplo es equivalente al anterior:

use App\Service;
use Mockery\MockInterface;

$mock = $this->mock(Service::class, function (MockInterface $mock) {
    $mock->shouldReceive('process')->once();
});

Puede usar el método partialMock cuando solo necesite simular algunos métodos de un objeto. Los métodos que no se simulan se ejecutarán normalmente cuando se llamen:

use App\Service;
use Mockery\MockInterface;

$mock = $this->partialMock(Service::class, function (MockInterface $mock) {
    $mock->shouldReceive('process')->once();
});

De manera similar, si desea espiar un objeto, la clase base de test de Laravel ofrece un método spy como un envoltorio conveniente alrededor del método Mockery::spy. Los espías son similares a los mocks; sin embargo, los espías registran cualquier interacción entre el espía y el código que se está probando, permitiéndole hacer aserciones después de que el código se ejecute:

use App\Service;

$spy = $this->spy(Service::class);

// ...

$spy->shouldHaveReceived('process');

#Simulación de facades

A diferencia de las llamadas tradicionales a métodos estáticos, los facades (incluidos los facades en tiempo real) pueden ser simulados. Esto ofrece una gran ventaja sobre los métodos estáticos tradicionales y le brinda la misma capacidad de prueba que tendría si usara inyección de dependencias tradicional. Al probar, a menudo querrá simular una llamada a un facade de Laravel que ocurre en uno de sus controladores. Por ejemplo, considere la siguiente acción de controlador:

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;

class UserController extends Controller
{
    /**
     * Recuperar una lista de todos los usuarios de la aplicación.
     */
    public function index(): array
    {
        $value = Cache::get('key');

        return [
            // ...
        ];
    }
}

Podemos simular la llamada al facade Cache usando el método shouldReceive, que devolverá una instancia de un mock de Mockery. Dado que los facades son resueltos y gestionados por el service container de Laravel, tienen mucha más capacidad de prueba que una clase estática típica. Por ejemplo, simulemos nuestra llamada al método get del facade Cache:

<?php

namespace Tests\Feature;

use Illuminate\Support\Facades\Cache;
use Tests\TestCase;

class UserControllerTest extends TestCase
{
    public function test_get_index(): void
    {
        Cache::shouldReceive('get')
                    ->once()
                    ->with('key')
                    ->andReturn('value');

        $response = $this->get('/users');

        // ...
    }
}
Внимание

No debe simular el facade Request. En su lugar, pase la entrada que desee a los métodos de prueba HTTP como get y post al ejecutar su prueba. De igual forma, en lugar de simular el facade Config, llame al método Config::set en sus pruebas.

#Espías de facades

Si desea espiar un facade, puede llamar al método spy en el facade correspondiente. Los espías son similares a los mocks; sin embargo, los espías registran cualquier interacción entre el espía y el código que se está probando, permitiéndole hacer aserciones después de que el código se ejecute:

use Illuminate\Support\Facades\Cache;

public function test_values_are_be_stored_in_cache(): void
{
    Cache::spy();

    $response = $this->get('/');

    $response->assertStatus(200);

    Cache::shouldHaveReceived('put')->once()->with('name', 'Taylor', 10);
}

#Interacción con el tiempo

Al probar, puede que ocasionalmente necesite modificar el tiempo que devuelven helpers como now o Illuminate\Support\Carbon::now(). Afortunadamente, la clase base de pruebas funcionales de Laravel incluye helpers que le permiten manipular el tiempo actual:

use Illuminate\Support\Carbon;

public function test_time_can_be_manipulated(): void
{
    // Travel into the future...
    $this->travel(5)->milliseconds();
    $this->travel(5)->seconds();
    $this->travel(5)->minutes();
    $this->travel(5)->hours();
    $this->travel(5)->days();
    $this->travel(5)->weeks();
    $this->travel(5)->years();

    // Viajar hacia el pasado...
    $this->travel(-5)->hours();

    // Viajar a un tiempo explícito...
    $this->travelTo(now()->subHours(6));

    // Volver al tiempo presente...
    $this->travelBack();
}

También puede proporcionar un closure a los distintos métodos de viaje en el tiempo. El closure se ejecutará con el tiempo congelado en el momento especificado. Una vez que el closure haya terminado, el tiempo continuará normalmente:

$this->travel(5)->days(function () {
    // Probar algo cinco días en el futuro...
});

$this->travelTo(now()->subDays(10), function () {
    // Probar algo en un momento dado...
});

El método freezeTime puede usarse para congelar el tiempo actual. De manera similar, el método freezeSecond congelará el tiempo actual pero al inicio del segundo actual:

use Illuminate\Support\Carbon;

// Congelar el tiempo y reanudar el tiempo normal después de ejecutar el closure...
$this->freezeTime(function (Carbon $time) {
    // ...
});

// Congelar el tiempo en el segundo actual y reanudar el tiempo normal después de ejecutar el closure...
$this->freezeSecond(function (Carbon $time) {
    // ...
})

Como puede esperar, todos los métodos mencionados arriba son principalmente útiles para probar comportamientos sensibles al tiempo en la aplicación, como bloquear posts inactivos en un foro de discusión:

use App\Models\Thread;

public function test_forum_threads_lock_after_one_week_of_inactivity()
{
    $thread = Thread::factory()->create();

    $this->travel(1)->week();

    $this->assertTrue($thread->isLockedByInactivity());
}