Zum Hauptinhalt springen

Warum Pest zum Laravel-Standard wurde

Seit Laravel 11 wird beim Anlegen eines Projekts mit laravel new Pest als Test-Framework standardmäßig ausgewählt. PHPUnit war lange das Standard-Testtool in PHP; Pest baut darauf auf und bietet eine kürzere, besser lesbare Schreibweise.
Da Pest auf PHPUnit läuft, funktionieren bestehende PHPUnit-Tests unverändert weiter. Eine Migration lässt sich schrittweise vornehmen.
Hinter der offiziellen Wahl von Pest steht das Ziel, die Hürden beim Schreiben von Tests zu reduzieren. Rituale wie „Klasse anlegen, Methode schreiben” entfallen; die Schreibweise lässt Sie sich auf das konzentrieren, was Sie testen wollen – so entsteht die Gewohnheit, Tests zu schreiben, viel natürlicher.

Wichtigste Unterschiede zu PHPUnit

Schreibweise der Tests

Der auffälligste Unterschied ist die Testsyntax.
test('Nutzer kann sich einloggen', function () {
    $user = User::factory()->create();

    $response = $this->post('/login', [
        'email' => $user->email,
        'password' => 'password',
    ]);

    $response->assertRedirect('/dashboard');
});
Die test()-Funktion von Pest nimmt eine Closure entgegen. Ohne Klassen- und Methodendefinition wird die Testabsicht schon in der ersten Zeile klar. it() funktioniert analog. Auf Englisch liest sich it('can login', ...) wie ein natürlicher Satz.

Die expect()-API

Das prägnanteste Merkmal von Pest sind die verkettbaren Assertions mit expect().
test('Post-Liste liefert JSON', function () {
    $posts = Post::factory(3)->create();

    $response = $this->getJson('/api/posts');

    expect($response->status())->toBe(200);
    expect($response->json('data'))->toHaveCount(3);
    expect($response->json('data.0.title'))->not->toBeEmpty();
});
Sie können Assertions in natürlicher englischer Form schreiben – expect($value)->toBe(), ->toBeNull(), ->toContain(), ->toHaveCount() – und mehrere Assertions per Method Chaining kombinieren.
expect($user)
    ->name->toBe('Tanaka Taro')
    ->email->toBe('[email protected]')
    ->is_admin->toBeFalse();

Setup und Teardown

Das Pendant zu PHPUnits setUp() ist beforeEach(), zu tearDown() afterEach().
beforeEach(function () {
    $this->user = User::factory()->create();
    $this->actingAs($this->user);
});

test('authentifizierter Nutzer kann sein Profil aufrufen', function () {
    $response = $this->get('/profile');
    $response->assertOk();
});

test('authentifizierter Nutzer kann sein Profil aktualisieren', function () {
    $response = $this->patch('/profile', ['name' => 'Neuer Name']);
    $response->assertRedirect('/profile');
});

Datasets – Table-driven Tests

Möchten Sie dieselbe Testlogik mit mehreren Daten ausführen, verwenden Sie dataset.
test('ungültige E-Mail-Adresse führt zum Validierungsfehler', function (string $email) {
    $response = $this->postJson('/api/users', [
        'name' => 'Tanaka Taro',
        'email' => $email,
    ]);

    $response->assertUnprocessable();
})->with([
    'keine E-Mail' => [''],
    'ungültiges Format' => ['notanemail'],
    'doppeltes @' => ['a@@example.com'],
]);
Die Labels der Datasets (z. B. 'keine E-Mail') werden an den Testnamen angehängt, sodass sofort ersichtlich ist, welche Variante fehlgeschlagen ist.

arch()-Tests – automatische Architektur-Checks

arch() in Pest sind Tests, die die Struktur der Codebasis prüfen. Sie können automatisch Architekturregeln validieren wie „hängt ein Controller direkt vom Modell ab?” oder „erbt das Modell von Eloquent?”.
test('Modelle erben von Eloquent', function () {
    arch()->expect('App\Models')
        ->toExtend(Illuminate\Database\Eloquent\Model::class);
});

test('Controller sind final', function () {
    arch()->expect('App\Http\Controllers')
        ->toBeFinal();
});

test('Service-Klassen hängen nicht von Controllern ab', function () {
    arch()->expect('App\Services')
        ->not->toUse('App\Http\Controllers');
});
arch()-Tests werden statisch über den Quellcode ausgeführt. Sie stellen keine echten HTTP-Requests und greifen nicht auf die Datenbank zu und sind daher sehr schnell.
Pest bringt außerdem Presets für häufig verwendete Architekturregeln mit.
test('Laravel-Architekturregeln werden befolgt', function () {
    arch()->preset()->laravel();
});
Das Preset laravel() bündelt gebräuchliche Regeln – Modellnamen, Controller-Vererbung, Middleware-Struktur – nach Laravel-typischen Konventionen.

Praxisbeispiele im Laravel-Projekt

Tests anlegen

php artisan make:test UserTest
Ab Laravel 11 wird bei Default-Konfiguration eine Testdatei im Pest-Format erzeugt.
<?php

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

Laravel-Test-Helper unverändert nutzen

Da Pest von Laravels TestCase erbt, stehen actingAs(), assertDatabaseHas(), die HTTP-Test-Helfer und alle weiteren Test-Helfer unverändert zur Verfügung.
use App\Models\Post;
use App\Models\User;

test('Nutzer kann eigenen Beitrag löschen', function () {
    $user = User::factory()->create();
    $post = Post::factory()->for($user)->create();

    $response = $this
        ->actingAs($user)
        ->delete("/posts/{$post->id}");

    $response->assertRedirect('/posts');
    $this->assertModelMissing($post);
});

test('Beitrag eines anderen Nutzers ist nicht löschbar', function () {
    $user = User::factory()->create();
    $otherUser = User::factory()->create();
    $post = Post::factory()->for($otherUser)->create();

    $this
        ->actingAs($user)
        ->delete("/posts/{$post->id}")
        ->assertForbidden();

    $this->assertModelExists($post);
});

Factories und Datenbank-Transaktionen

Traits wie RefreshDatabase oder DatabaseTransactions lassen sich bequem über uses() einbinden. Am Dateianfang angewendet, gilt es für die gesamte Datei.
uses(Tests\TestCase::class, Illuminate\Foundation\Testing\RefreshDatabase::class)->in('Feature');
Einmal in tests/Pest.php konfiguriert, wird RefreshDatabase automatisch in allen Feature-Tests aktiviert. Einzelne Testdateien können das überschreiben.

Koexistenz mit bestehenden PHPUnit-Tests

Pest und PHPUnit lassen sich im selben Projekt einsetzen. Bestehende PHPUnit-Tests müssen nicht umgeschrieben werden; Sie schreiben lediglich neue Tests im Pest-Format.
./vendor/bin/pest       # alle Tests mit Pest ausführen (auch PHPUnit-Tests)
./vendor/bin/pest --filter="Nutzer"  # nach Testnamen filtern
php artisan test        # auch über das Artisan-Kommando läuft Pest
Ab Laravel 11 verwendet php artisan test automatisch Pest, sofern Pest installiert ist.

Wie Sie die Migration angehen

  1. In tests/Pest.php die uses()-Konfiguration ergänzen
  2. Neue Tests im Pest-Format schreiben
  3. Bestehende PHPUnit-Tests behutsam und mit Prüfung migrieren
Es ist nicht nötig, alle Tests hastig umzuschreiben. Da die Syntax von Pest gegenüber PHPUnit weniger Lernaufwand erfordert, verbreitet sie sich im Team meist reibungslos.

Fazit

VergleichspunktPHPUnitPest
TestdefinitionKlasse + MethodeFunktionen test() / it()
Assertions$this->assert*()expect()->to*()
SetupsetUp()beforeEach()
Data-driven@dataProvider->with()
Architektur-Checksnicht vorhandenarch()
AusführungsgeschwindigkeitStandardgleichwertig (läuft auf PHPUnit)
Pest ersetzt PHPUnit nicht, sondern ist eine darauf aufsetzende Schicht. Die Laravel-Integration ist tief; im Starterkit-Setup wird Pest als Default gewählt. Die Einführung in Bestandsprojekte ist günstig – schon der Einstieg mit neuen Tests bringt Nutzen. Wenn mehr Tests geschrieben werden, werden Bugs früher entdeckt und die Hürde vor Refactorings sinkt. Pest ist ein Werkzeug, um genau diese „Reibung beim Testschreiben” zu reduzieren.

Offizielle Pest-Dokumentation

Zu Datasets, Coverage, paralleler Ausführung und allen weiteren Funktionen von Pest konsultieren Sie die offizielle Dokumentation.
Zuletzt geändert am 13. Juli 2026