> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Mocking

> Utilisez les fonctionnalités de mock et test doubles de Laravel pour exécuter des tests isolés.

## Pourquoi mocker

Lors de la rédaction de tests, certains traitements ne doivent pas être exécutés (envoi de mail, cache, API externe).
On remplace ces traitements par des « imitations » : c'est le mock.

Avantages :

* **Rapidité** — pas d'appel externe ni de traitement lourd
* **Stabilité** — indépendance vis-à-vis de l'état des services externes
* **Portée claire** — chaque test cible une classe/méthode

Laravel expose des helpers pour mocker événements, jobs, façades, etc. En interne, il utilise [Mockery](https://github.com/mockery/mockery), utilisable sans setup complexe.

```mermaid theme={null}
graph LR
    A["Test"] --> B["Mock<br>(imitation)"]
    A --> C["Code de production"]
    B --> D["Non exécuté<br>(API externe, mail…)"]
    C --> B
    style B fill:#f9a,stroke:#c66
    style D fill:#eee,stroke:#aaa
```

## Mocker des objets

Pour mocker un objet injecté via le conteneur, enregistrez l'instance de mock dans le conteneur.
Il retournera alors le mock au lieu d'instancier la classe.

<CodeGroup>
  ```php Pest theme={null}
  use App\Service;
  use Mockery;
  use Mockery\MockInterface;

  test('something can be mocked', function () {
      $this->instance(
          Service::class,
          Mockery::mock(Service::class, function (MockInterface $mock) {
              $mock->expects('process');
          })
      );
  });
  ```

  ```php PHPUnit theme={null}
  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->expects('process');
          })
      );
  }
  ```
</CodeGroup>

### Méthode `mock()`

Équivalent plus concis via la méthode de la classe de test de base.

```php theme={null}
use App\Service;
use Mockery\MockInterface;

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

### Méthode `partialMock()`

Pour ne mocker que certaines méthodes ; les autres s'exécutent normalement.

```php theme={null}
use App\Service;
use Mockery\MockInterface;

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

### Méthode `spy()`

Un spy ressemble à un mock mais on vérifie les interactions **après** l'exécution.

```php theme={null}
use App\Service;

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

// ... exécute le code testé ...

$spy->shouldHaveReceived('process');
```

```mermaid theme={null}
graph TD
    subgraph "Mock"
        M1["Configurer les attentes<br>expects('process')"] --> M2["Exécution"] --> M3["Vérification auto"]
    end
    subgraph "Spy"
        S1["Configurer le spy"] --> S2["Exécution"] --> S3["Vérification a posteriori<br>shouldHaveReceived('process')"]
    end
```

## Mocker une façade

Les [façades](/fr/facades), contrairement aux méthodes statiques classiques, sont mockables.
Vous gardez la testabilité de l'injection de dépendances avec une syntaxe concise.

Contrôleur utilisant le cache :

```php theme={null}
<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;

class UserController extends Controller
{
    /**
     * Retourne la liste des utilisateurs.
     */
    public function index(): array
    {
        $value = Cache::get('key');

        return [
            // ...
        ];
    }
}
```

Utilisez `expects()` pour mocker `Cache::get`.

<CodeGroup>
  ```php Pest theme={null}
  <?php

  use Illuminate\Support\Facades\Cache;

  test('get index', function () {
      Cache::expects('get')
          ->with('key')
          ->andReturn('value');

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

      // ...
  });
  ```

  ```php PHPUnit theme={null}
  <?php

  namespace Tests\Feature;

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

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

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

          // ...
      }
  }
  ```
</CodeGroup>

<Warning>
  Ne mockez pas la façade `Request` : passez les valeurs via les méthodes HTTP de test (`get`, `post`, …). De même, appelez `Config::set()` dans le test plutôt que de mocker la façade `Config`.
</Warning>

## Espionner une façade

Utilisez `spy()` sur la façade.

<CodeGroup>
  ```php Pest theme={null}
  <?php

  use Illuminate\Support\Facades\Cache;

  test('values are stored in cache', function () {
      Cache::spy();

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

      $response->assertStatus(200);

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

  ```php PHPUnit theme={null}
  use Illuminate\Support\Facades\Cache;

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

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

      $response->assertStatus(200);

      Cache::shouldHaveReceived('put')->with('name', 'Taylor', 10);
  }
  ```
</CodeGroup>

## Manipulation du temps

Pour tester une logique dépendante du temps, changez ce que `now()` ou `Carbon::now()` retourne.
La classe de base des tests fonctionnels fournit des helpers.

### `travel()` — Déplacer le temps

<CodeGroup>
  ```php Pest theme={null}
  test('time can be manipulated', function () {
      // Avancer
      $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();

      // Reculer
      $this->travel(-5)->hours();

      // À un instant précis
      $this->travelTo(now()->subHours(6));

      // Retour au présent
      $this->travelBack();
  });
  ```

  ```php PHPUnit theme={null}
  public function test_time_can_be_manipulated(): void
  {
      // Avancer
      $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();

      // Reculer
      $this->travel(-5)->hours();

      // À un instant précis
      $this->travelTo(now()->subHours(6));

      // Retour au présent
      $this->travelBack();
  }
  ```
</CodeGroup>

### Avec une closure

Passez une closure pour tester dans un contexte temporel donné puis revenir automatiquement.

```php theme={null}
$this->travel(5)->days(function () {
    // État dans 5 jours
});

$this->travelTo(now()->subDays(10), function () {
    // À un instant précis
});
```

### `freezeTime()` — Figer le temps

`freezeTime()` fige l'heure actuelle ; `freezeSecond()` la fige au début de la seconde.

```php theme={null}
use Illuminate\Support\Carbon;

$this->freezeTime(function (Carbon $time) {
    // ...
});

$this->freezeSecond(function (Carbon $time) {
    // ...
});
```

### Exemple : verrouillage des threads inactifs

Utile pour tester une fonction où un thread se verrouille après une période d'inactivité.

<CodeGroup>
  ```php Pest theme={null}
  use App\Models\Thread;

  test('forum threads lock after one week of inactivity', function () {
      $thread = Thread::factory()->create();

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

      expect($thread->isLockedByInactivity())->toBeTrue();
  });
  ```

  ```php PHPUnit theme={null}
  use App\Models\Thread;

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

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

      $this->assertTrue($thread->isLockedByInactivity());
  }
  ```
</CodeGroup>

<Info>
  Les méthodes de time travel sont disponibles dans les tests fonctionnels (héritant de `Tests\TestCase`), pas dans le `TestCase` de PHPUnit de base.
</Info>

## Récapitulatif

### Mock

| Méthode                                | Description                               |
| -------------------------------------- | ----------------------------------------- |
| `$this->mock(Class::class, fn)`        | Mock complet enregistré dans le conteneur |
| `$this->partialMock(Class::class, fn)` | Mock partiel                              |
| `$this->spy(Class::class)`             | Spy enregistré dans le conteneur          |
| `$this->instance(Class::class, $mock)` | Enregistre une instance arbitraire        |
| `Facade::expects('method')`            | Mocke une méthode de façade               |
| `Facade::spy()`                        | Espionne une façade                       |
| `$spy->shouldHaveReceived('method')`   | Vérifie sur un spy                        |

### Temps

| Méthode                    | Description                       |
| -------------------------- | --------------------------------- |
| `$this->travel(n)->unit()` | Avance / recule dans le temps     |
| `$this->travelTo(Carbon)`  | Aller à un instant précis         |
| `$this->travelBack()`      | Retour au présent                 |
| `$this->freezeTime(fn)`    | Fige le temps pendant l'exécution |
| `$this->freezeSecond(fn)`  | Fige au début de la seconde       |


## Related topics

- [Mise à niveau de Laravel 9 vers 10](/fr/blog/upgrade-9-to-10.md)
- [Core — opérations AT Protocol de base](/fr/packages/laravel-bluesky/core.md)
- [Façades](/fr/facades.md)
