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

# Mock

> Come eseguire test in modo isolato usando le funzionalità di mock e test double di Laravel

## Perché usare i mock

Quando scrivi test, ci sono operazioni che non vuoi eseguire realmente: invio email, letture/scritture in cache, chiamate ad API esterne.
Sostituire queste operazioni con "impostori che fingono di essere veri" si chiama mocking.

Vantaggi dei mock:

* **Test veloci** — non esegui servizi esterni od operazioni pesanti, quindi i test finiscono subito
* **Test stabili** — non dipendi dallo stato dei servizi esterni, il risultato è sempre lo stesso
* **Test più mirati** — verifichi una singola classe o metodo

Laravel fornisce helper pronti all'uso per mocking di eventi, job e facade.
Internamente usa [Mockery](https://github.com/mockery/mockery); li usi senza configurazione complessa.

```mermaid theme={null}
graph LR
    A["Test"] --> B["Mock<br>(finto)"]
    A --> C["Codice di produzione"]
    B --> D["Non eseguito<br>(API esterne, email, ecc.)"]
    C --> B
    style B fill:#f9a,stroke:#c66
    style D fill:#eee,stroke:#aaa
```

## Oggetti mock

Per fare il mock di un oggetto iniettato tramite il service container, registra un'istanza mock nel container.
Il container userà l'istanza mock invece di crearne una reale.

<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>

### Metodo `mock()`

Con il metodo `mock()` fornito dalla classe base dei test di Laravel scrivi lo stesso in modo più conciso.

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

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

### Metodo `partialMock()`

Se vuoi mockare solo alcuni metodi di un oggetto, usa `partialMock()`.
I metodi non mockati vengono eseguiti normalmente.

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

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

### Metodo `spy()`

Uno spy è simile a un mock, ma verifica le interazioni dopo l'esecuzione del codice.
Il mock imposta in anticipo "questo metodo dovrà essere chiamato"; lo spy verifica a posteriori "questo metodo è stato chiamato?".

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

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

// ...Esegui il codice sotto test...

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

```mermaid theme={null}
graph TD
    subgraph "Mock"
        M1["Imposta le aspettative in anticipo<br>expects('process')"] --> M2["Esegui il codice"] --> M3["Verifica automatica"]
    end
    subgraph "Spy"
        S1["Configura lo spy"] --> S2["Esegui il codice"] --> S3["Verifica a posteriori<br>shouldHaveReceived('process')"]
    end
```

## Mock delle facade

Le [facade](/it/facades), diversamente dalle normali chiamate a metodi statici, possono essere mockate.
Hai la stessa testabilità della dependency injection con una sintassi concisa.

Considera ad esempio un controller che usa la cache.

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

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;

class UserController extends Controller
{
    /**
     * Restituisce l'elenco di tutti gli utenti
     */
    public function index(): array
    {
        $value = Cache::get('key');

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

Per mockare il metodo `get` della facade `Cache` usa `expects()`.

<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>
  Non mockare la facade `Request`. Passa invece gli input ai metodi di test HTTP come `get` o `post`. Analogamente, invece di mockare la facade `Config`, chiama `Config::set()` nel test.
</Warning>

## Spy delle facade

Per monitorare una facade con uno spy, chiama il metodo `spy()` sulla facade corrispondente.
Lo spy è utile quando vuoi verificare le interazioni dopo l'esecuzione del codice.

<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>

## Manipolazione del tempo

Nei test che verificano logiche dipendenti dal tempo è utile poter cambiare l'orario restituito da `now()` o `Carbon::now()`.
La classe base dei test feature di Laravel fornisce helper per manipolare il tempo.

### `travel()` — spostare il tempo

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

      // Torna indietro nel tempo
      $this->travel(-5)->hours();

      // Sposta a un istante specifico
      $this->travelTo(now()->subHours(6));

      // Torna all'orario corrente
      $this->travelBack();
  });
  ```

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

      // Torna indietro nel tempo
      $this->travel(-5)->hours();

      // Sposta a un istante specifico
      $this->travelTo(now()->subHours(6));

      // Torna all'orario corrente
      $this->travelBack();
  }
  ```
</CodeGroup>

### Spostamento del tempo con closure

Passando una closure ai metodi di travel, il tempo viene fissato all'istante indicato durante l'esecuzione della closure e ripristinato alla fine.

```php theme={null}
$this->travel(5)->days(function () {
    // Testa lo stato dopo 5 giorni
});

$this->travelTo(now()->subDays(10), function () {
    // Testa in un istante specifico
});
```

### `freezeTime()` — fermare il tempo

`freezeTime()` congela l'orario corrente. `freezeSecond()` lo congela all'inizio del secondo corrente.

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

// Congela il tempo, esegue la closure e riprende
$this->freezeTime(function (Carbon $time) {
    // ...
});

// Congela all'inizio del secondo corrente ed esegue la closure
$this->freezeSecond(function (Carbon $time) {
    // ...
});
```

### Esempio pratico: lock su thread inattivi

La manipolazione del tempo aiuta a testare funzionalità come i thread di un forum che si bloccano dopo un certo periodo di inattività.

<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>
  I metodi di manipolazione del tempo come `travel()` sono utilizzabili solo nei feature test (classi che estendono `Tests\TestCase`). Non funzionano nel `TestCase` base di PHPUnit.
</Info>

## Elenco dei metodi

### Legati ai mock

| Metodo                                 | Descrizione                                                    |
| -------------------------------------- | -------------------------------------------------------------- |
| `$this->mock(Class::class, fn)`        | Crea un mock completo della classe e lo registra nel container |
| `$this->partialMock(Class::class, fn)` | Mocka solo alcuni metodi                                       |
| `$this->spy(Class::class)`             | Crea uno spy e lo registra nel container                       |
| `$this->instance(Class::class, $mock)` | Registra un'istanza mock qualsiasi nel container               |
| `Facade::expects('method')`            | Mocka un metodo della facade                                   |
| `Facade::spy()`                        | Monitora la facade con uno spy                                 |
| `$spy->shouldHaveReceived('method')`   | Verifica che uno spy abbia ricevuto la chiamata                |

### Legati al tempo

| Metodo                     | Descrizione                                         |
| -------------------------- | --------------------------------------------------- |
| `$this->travel(n)->unit()` | Sposta il tempo dell'unità indicata                 |
| `$this->travelTo(Carbon)`  | Vai a un istante specifico                          |
| `$this->travelBack()`      | Torna all'orario corrente                           |
| `$this->freezeTime(fn)`    | Congela il tempo ed esegue la closure               |
| `$this->freezeSecond(fn)`  | Congela all'inizio del secondo ed esegue la closure |


## Related topics

- [HTTP Client](/it/http-client.md)
- [Aggiornamento da Laravel 9 a 10](/it/blog/upgrade-9-to-10.md)
- [Test avanzati con Pest](/it/advanced/testing-pest.md)
- [Testing](/it/packages/laravel-bluesky/testing.md)
- [Creare un provider personalizzato per l'AI SDK](/it/advanced/ai-sdk-custom-provider.md)
