Vai al contenuto principale

Cos’è il testing

Il testing è il meccanismo che verifica automaticamente che il codice funzioni come previsto. Scrivere test ti permette di verificare rapidamente che aggiungere o modificare funzionalità non rompa il comportamento esistente. Nello sviluppo in team i test danno sicurezza nel modificare il codice e nelle code review. Laravel supporta il testing fin dall’inizio: puoi usare sia Pest sia PHPUnit. Alla creazione del progetto trovi automaticamente il file phpunit.xml e la directory tests/.
Pest è costruito su PHPUnit e permette di scrivere test con una sintassi più concisa e leggibile. Se inizi ora, ti consigliamo Pest.

Struttura della directory tests/

tests/
├── Feature/      # Feature test
│   └── ExampleTest.php
├── Unit/         # Unit test
│   └── ExampleTest.php
└── TestCase.php
  • Feature/ — Feature test: test di grande granularità che includono richieste HTTP. Verificano il funzionamento di più oggetti insieme o endpoint API, quindi test più vicini all’applicazione completa. La maggior parte dei test starà qui.
  • Unit/ — Unit test: test di piccola granularità per singola classe o metodo. L’applicazione Laravel non viene avviata, quindi non puoi usare database o altre funzionalità del framework.

Creazione di un test

Con il comando Artisan make:test generi una nuova classe di test.
# Crea un Feature test (default)
php artisan make:test TodoTest

# Crea uno Unit test
php artisan make:test TodoTest --unit
Viene generato tests/Feature/TodoTest.php.

Esecuzione dei test

Esegui i test con il comando php artisan test.
php artisan test
Puoi eseguire direttamente vendor/bin/pest o vendor/bin/phpunit per lo stesso risultato, ma php artisan test mostra un output più leggibile ed è consigliato. Per eseguire solo una suite:
# Esegui solo i Feature test
php artisan test --testsuite=Feature

# Fermati al primo fallimento
php artisan test --stop-on-failure

Come scrivere test base

Esempio di asserzioni semplici.
<?php

test('true è true', function () {
    expect(true)->toBeTrue();
});

test('verifica che la stringa contenga', function () {
    $message = 'Hello, Laravel!';

    expect($message)->toContain('Laravel');
});
<?php

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;

class ExampleTest extends TestCase
{
    public function test_true_is_true(): void
    {
        $this->assertTrue(true);
    }

    public function test_string_contains_laravel(): void
    {
        $message = 'Hello, Laravel!';

        $this->assertStringContainsString('Laravel', $message);
    }
}

Asserzioni più comuni

PestPHPUnitDescrizione
expect($x)->toBe($y)$this->assertSame($y, $x)Uguali in senso stretto
expect($x)->toEqual($y)$this->assertEquals($y, $x)Uguali (ignora il tipo)
expect($x)->toBeTrue()$this->assertTrue($x)È true
expect($x)->toBeFalse()$this->assertFalse($x)È false
expect($x)->toBeNull()$this->assertNull($x)È null
expect($x)->toContain($y)$this->assertStringContainsString($y, $x)Contiene la stringa
expect($x)->toHaveCount($n)$this->assertCount($n, $x)Numero di elementi corretto

Test HTTP

Con i test HTTP di Laravel puoi simulare richieste alle route senza avviare un vero server HTTP. È una funzionalità potente delle classi di test nella directory Feature/.

Verifica della visualizzazione di una pagina

Con get() invii una richiesta GET e verifichi la response.
<?php

test('la pagina principale viene mostrata', function () {
    $response = $this->get('/');

    $response->assertStatus(200);
});
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_top_page_is_displayed(): void
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

Esempio di test HTTP di un’app ToDo

Con un’app che ha il modello Todo, testiamo le operazioni CRUD.
<?php

use App\Models\Todo;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

test('viene mostrato l\'elenco dei ToDo', function () {
    Todo::factory()->count(3)->create();

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

    $response->assertStatus(200);
    $response->assertSee('Elenco');
});

test('è possibile creare un ToDo', function () {
    $response = $this->post('/todos', [
        'title' => 'Studiare Laravel',
    ]);

    $response->assertRedirect('/todos');
    $this->assertDatabaseHas('todos', ['title' => 'Studiare Laravel']);
});

test('è possibile eliminare un ToDo', function () {
    $todo = Todo::factory()->create();

    $response = $this->delete("/todos/{$todo->id}");

    $response->assertRedirect('/todos');
    $this->assertDatabaseMissing('todos', ['id' => $todo->id]);
});
<?php

namespace Tests\Feature;

use App\Models\Todo;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class TodoTest extends TestCase
{
    use RefreshDatabase;

    public function test_todo_list_is_displayed(): void
    {
        Todo::factory()->count(3)->create();

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

        $response->assertStatus(200);
        $response->assertSee('Elenco');
    }

    public function test_todo_can_be_created(): void
    {
        $response = $this->post('/todos', [
            'title' => 'Studiare Laravel',
        ]);

        $response->assertRedirect('/todos');
        $this->assertDatabaseHas('todos', ['title' => 'Studiare Laravel']);
    }

    public function test_todo_can_be_deleted(): void
    {
        $todo = Todo::factory()->create();

        $response = $this->delete("/todos/{$todo->id}");

        $response->assertRedirect('/todos');
        $this->assertDatabaseMissing('todos', ['id' => $todo->id]);
    }
}

Asserzioni comuni sulle response

MetodoDescrizione
assertStatus(200)Verifica il codice HTTP della response
assertOk()Verifica che il codice sia 200
assertRedirect('/path')Verifica il redirect all’URL
assertSee('testo')Verifica che il corpo della response contenga il testo
assertDontSee('testo')Verifica che il corpo non contenga il testo
assertJson([...])Verifica i dati della response JSON
assertDatabaseHas('table', [...])Verifica che il record esista nel DB
assertDatabaseMissing('table', [...])Verifica che il record non esista nel DB
Con il trait RefreshDatabase il database viene resettato dopo ogni test. Serve a evitare che i dati interferiscano tra i test.

Ambiente di test

Configurazione di phpunit.xml

Configura l’ambiente di test nel file phpunit.xml nella root del progetto. Di default sessione e cache usano il driver array, così i dati non permangono tra i test.
<env name="APP_ENV" value="testing"/>
<env name="CACHE_STORE" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
Per il database è comune usare SQLite in-memory, che non lascia file.
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>

File .env.testing

Creando .env.testing nella root del progetto, verrà usato al posto di .env durante i test. Utile se vuoi separare connessioni al database o servizi esterni dedicati ai test.
APP_ENV=testing
DB_CONNECTION=sqlite
DB_DATABASE=:memory:
Se hai cache di configurazione, esegui php artisan config:clear prima dei test. Altrimenti potrebbe essere usata una cache obsoleta.

Esecuzione parallela dei test

Con l’aumentare del numero di test cresce anche il tempo di esecuzione. Con l’opzione --parallel esegui i test su più processi contemporaneamente per ridurre i tempi. Installa prima il pacchetto brianium/paratest.
composer require brianium/paratest --dev
Esegui con l’opzione --parallel.
php artisan test --parallel
Di default vengono creati tanti processi quanti sono i core della CPU. Per indicare il numero usa --processes.
php artisan test --parallel --processes=4
Con i test paralleli ogni processo deve usare un database indipendente. La combinazione del trait RefreshDatabase con SQLite in-memory in phpunit.xml funziona senza problemi.

Prossimi passi

Test HTTP

Approfondisci simulazione delle richieste, test autenticati, asserzioni JSON e altri aspetti.
Ultima modifica il 13 luglio 2026