# Laravel Testing 2026: Pest, Mocking und technische Interviewfragen > Ein umfassender Leitfaden zu Laravel Testing mit Pest 4: Unit Tests, Feature Tests, Mocking-Strategien, Architektur-Tests und haeufige Interviewfragen. - Published: 2026-06-04 - Updated: 2026-06-04 - Author: SharpSkill - Tags: laravel, testing, pest, php, interview - Reading time: 8 min --- Die Testlandschaft in Laravel hat sich mit Pest 4, Mutation Testing und Architektur-Presets grundlegend veraendert. Laravel 12 wird mit erstklassiger Pest-Unterstuetzung ausgeliefert, wodurch das Testen deutlich ausdruecklicher und weniger umstaendlich als mit reinem PHPUnit ausfaellt. Dieser Leitfaden behandelt die Muster, die sowohl fuer Produktionsanwendungen als auch fuer technische Vorstellungsgespraeche relevant sind. > **Pest 4 ist der Standard** > > Pest 4.7 (basierend auf PHPUnit 12) ist das Standard-Testing-Framework fuer Laravel 12. Es erfordert PHP 8.3+ und bringt Mutation Testing, zeitbalanciertes Sharding und Architektur-Presets von Haus aus mit. ## Pest 4 in einem Laravel 12 Projekt einrichten Jede neue Laravel 12 Anwendung wird automatisch mit Pest aufgesetzt. Fuer bestehende Projekte dauert die Migration von PHPUnit oder Pest 3 nur wenige Minuten. Die Konfiguration befindet sich in `tests/Pest.php`, wo globale Traits und Hilfsmethoden registriert werden. ```php // tests/Pest.php use Illuminate\Foundation\Testing\RefreshDatabase; pest() ->extend(Tests\TestCase::class) ->use(RefreshDatabase::class) ->in('Feature'); ``` Diese einzelne Datei ersetzt den alten `CreatesApplication` Trait und die Vererbung der Basis-Testklasse. Der `RefreshDatabase` Trait kapselt jeden Test in eine Datenbanktransaktion, die Aenderungen automatisch zurueckrollt. ```php // tests/Feature/UserRegistrationTest.php use App\Models\User; it('registers a new user with valid data', function () { = ->postJson('/api/register', [ 'name' => 'Jane Doe', 'email' => 'jane@example.com', 'password' => 'SecurePass123!', 'password_confirmation' => 'SecurePass123!', ]); ->assertStatus(201) ->assertJsonStructure(['user' => ['id', 'name', 'email']]); expect(User::where('email', 'jane@example.com')->exists())->toBeTrue(); }); ``` Pests `expect()` API harmoniert nahtlos mit PHPUnit-Assertions. Der `assertJsonStructure` Aufruf validiert die Antwortstruktur, waehrend `expect()->toBeTrue()` den Datenbankzustand bestaetigt. Beide Stile koexistieren ohne Konflikte. ## Unit Tests vs Feature Tests in Laravel Die Unterscheidung zwischen Unit- und Feature-Tests in Laravel bestimmt, welche Teile des Frameworks gestartet werden. Unit Tests laufen ohne den Application Container, was sie schneller macht, aber auf reine Logik beschraenkt. Feature Tests starten die vollstaendige Anwendung und ermoeglichen HTTP-Aufrufe, Datenbankabfragen und Service-Aufloesungen. ```php // tests/Unit/PriceCalculatorTest.php use App\Services\PriceCalculator; describe('PriceCalculator', function () { it('applies a percentage discount correctly', function () { = new PriceCalculator(); // 20% off a 150.00 base price = ->applyDiscount(150.00, 20); expect()->toBe(120.00); }); it('rejects negative discount values', function () { = new PriceCalculator(); expect(fn () => ->applyDiscount(100.00, -5)) ->toThrow(InvalidArgumentException::class); }); }); ``` Unit Tests zielen auf isolierte Klassen ohne externe Abhaengigkeiten ab. Der `describe` Block gruppiert zusammengehoerige Assertions, und Pest 4 unterstuetzt verschachtelte `describe` Bloecke fuer komplexe Testhierarchien. Feature Tests sollten den Grossteil einer Laravel Test Suite ausmachen. Sie erkennen Integrationsfehler, die Unit Tests uebersehen, wie etwa fehlerhafte Route-Middleware, fehlende Validierungsregeln oder defekte Eloquent-Beziehungen. Eine gaengige Faustregel lautet: Wenn der Code die Datenbank, die HTTP-Schicht oder eine Facade beruehrt, gehoert ein Feature Test dazu. ## Mocking-Strategien mit Facades und Mockery Mocking beim Laravel-Testing isoliert den zu testenden Code von externen Abhaengigkeiten. Laravel Facades bieten integrierte Fake-Implementierungen fuer Queues, Events, Benachrichtigungen, Mail und Storage. [Mockery](https://github.com/mockery/mockery) uebernimmt alles andere. ```php // tests/Feature/OrderProcessingTest.php use App\Models\Order; use App\Models\User; use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Queue; use App\Mail\OrderConfirmation; use App\Jobs\ProcessPayment; it('dispatches payment job and sends confirmation email', function () { // Fake both Mail and Queue facades Mail::fake(); Queue::fake(); = User::factory()->create(); = Order::factory()->for()->create([ 'total' => 99.99, 'status' => 'pending', ]); // Act: confirm the order via HTTP ->actingAs() ->postJson("/api/orders/{->id}/confirm") ->assertOk(); // Assert: payment job was dispatched with correct amount Queue::assertPushed(ProcessPayment::class, function () use () { return ->order->id === ->id && ->order->total === 99.99; }); // Assert: confirmation email was sent to the user Mail::assertSent(OrderConfirmation::class, function () use () { return ->hasTo(->email); }); }); ``` Facade Fakes fangen Aufrufe auf Framework-Ebene ab und verhindern, dass tatsaechlich E-Mails versendet oder Jobs in die Warteschlange gestellt werden. Die Closure-basierten Assertions ueberpruefen die exakten Daten, die an jede Komponente uebergeben werden. > **An Grenzen mocken** > > Interne Domain-Klassen sollten nicht gemockt werden. Das Mocking gehoert an die Systemgrenzen: Drittanbieter-APIs, Mail, Queues, Dateisysteme. Uebertriebenes Mocking macht Tests fragil und koppelt sie zu eng an Implementierungsdetails. Fuer Abhaengigkeiten, die keine Facades sind, erfolgt die Injektion ueber den Konstruktor mit Mockery: ```php // tests/Feature/PaymentGatewayTest.php use App\Services\PaymentGateway; use App\Services\StripeClient; it('charges the customer through the payment gateway', function () { // Create a mock of the Stripe client = Mockery::mock(StripeClient::class); ->shouldReceive('charge') ->once() ->with('cus_abc123', 5000, 'usd') ->andReturn(['status' => 'succeeded', 'id' => 'ch_xyz']); // Bind the mock in the container ->app->instance(StripeClient::class, ); = app(PaymentGateway::class); = ->processCharge('cus_abc123', 50.00); expect(['status'])->toBe('succeeded'); }); ``` Das Binden des Mocks mit `->app->instance()` ersetzt die echte Implementierung fuer die Dauer des Tests. Mockery verifiziert, dass `charge` genau einmal mit den erwarteten Argumenten aufgerufen wurde. ## Architektur-Tests mit Pest Presets Pest 4 enthaelt [Architektur-Tests](https://pestphp.com/docs/arch-testing), die strukturelle Regeln im gesamten Codebase durchsetzen. Diese Tests laufen gegen den AST, nicht gegen die Laufzeit, was sie extrem schnell macht. ```php // tests/Architecture/ArchitectureTest.php arch('controllers do not use Eloquent directly') ->expect('App\Http\Controllers') ->not->toUse('Illuminate\Database\Eloquent'); arch('services are final classes') ->expect('App\Services') ->toBeFinal(); arch('no debugging functions in production code') ->expect(['dd', 'dump', 'var_dump', 'ray']) ->not->toBeUsed(); ``` Die erste Regel verhindert, dass Controller direkt auf die Datenbank zugreifen, und erzwingt damit ein Service-Layer-Pattern. Die zweite stellt sicher, dass Services nicht erweitert werden koennen, was die Vererbungskomplexitaet reduziert. Die dritte faengt uebrig gebliebene Debug-Anweisungen ab, bevor sie in die Produktion gelangen. Laravel-spezifische Presets stehen ebenfalls zur Verfuegung: ```php // tests/Architecture/LaravelPresetTest.php arch()->preset()->laravel(); arch()->preset()->security(); arch()->preset()->php(); ``` Diese drei Zeilen setzen Dutzende von Regeln durch: Models muessen die korrekte Basisklasse erweitern, Controller duerfen keine Geschaeftslogik enthalten, unsichere Funktionen wie `eval()` oder `md5()` zum Hashing sind verboten, und Standard-PHP-Konventionen werden eingehalten. ## Mutation Testing zur Ueberpruefung der Testqualitaet Code Coverage misst, welche Zeilen waehrend der Tests ausgefuehrt werden. [Mutation Testing](https://pestphp.com/docs/mutation-testing) geht weiter: Es veraendert den Quellcode und prueft, ob die Tests die Aenderung erkennen. Wenn eine Mutation ueberlebt, hat die Test Suite eine Luecke. ```bash # Run mutation testing on a specific class php artisan test --mutate --class=App\\Services\\PriceCalculator ``` Pest 4 fuehrt Mutationen ein wie das Aendern von `>` zu `>=`, das Entfernen von `return`-Anweisungen und das Umkehren von booleschen Bedingungen. Ein Mutation Score unter 80% deutet in der Regel darauf hin, dass Tests zwar Ausgaben verifizieren, aber Randfaelle nicht abpruefen. ```php // Example: this test has high coverage but low mutation score it('calculates shipping cost', function () { = calculateShipping(weight: 5.0, zone: 'domestic'); // Only checks that the result is numeric expect()->toBeFloat(); }); // Improved: catches mutations by asserting the exact value it('calculates domestic shipping for 5kg package', function () { = calculateShipping(weight: 5.0, zone: 'domestic'); // Exact assertion catches operator and value mutations expect()->toBe(12.50); }); ``` Der erste Test besteht selbst dann, wenn die Berechnungslogik voellig falsch ist, solange ein Float zurueckgegeben wird. Der zweite Test schlaegt sofort fehl, wenn eine Mutation die Formel veraendert. Spezifische Assertions fuehren zu hoeheren Mutation Scores. ## Datenbank-Testing-Patterns und Factories Laravel Model Factories generieren realistische Testdaten ohne manuellen Array-Aufbau. Pest 4 in Kombination mit Factories erzeugt lesbaren, wartbaren Datenaufbau. ```php // tests/Feature/ArticlePublishingTest.php use App\Models\Article; use App\Models\User; it('publishes a draft article and updates the timestamp', function () { = User::factory()->create(['role' => 'editor']); = Article::factory() ->for(, 'author') ->draft() ->create(['title' => 'Testing Best Practices']); ->actingAs() ->patchJson("/api/articles/{->id}/publish") ->assertOk() ->assertJsonPath('data.status', 'published'); ->refresh(); expect(->status)->toBe('published') ->and(->published_at)->not->toBeNull() ->and(->published_at->isToday())->toBeTrue(); }); ``` Der `draft()` Factory State setzt Standardwerte fuer unveroeffentlichte Artikel. Verkettete `expect()` Assertions mit `->and()` lesen sich wie natuerliche Sprache und liefern bei Fehlern aussagekraeftige Meldungen. > **Paralleles Testing** > > Mit `php artisan test --parallel` werden Tests ueber mehrere Prozesse verteilt. Laravel erstellt automatisch separate Testdatenbanken fuer jeden Prozess, um Datenkonflikte zu vermeiden. In Kombination mit Pest 4s zeitbalanciertem Sharding ergibt sich eine optimale CI-Performance. ## Haeufige Laravel Testing Interviewfragen Technische Vorstellungsgespraeche fuer [Laravel-Positionen](/technologies/laravel) prufen haeufig das Testing-Wissen ab. Die folgenden Fragen tauchen am haeufigsten auf, zusammen mit dem, was eine starke Antwort abdecken sollte. **Was ist der Unterschied zwischen `fake()`, `mock()` und `spy()` beim Laravel-Testing?** Facade `fake()` ersetzt die gesamte Facade durch eine In-Memory-Implementierung (Mail::fake, Queue::fake). `mock()` ueber Mockery setzt Erwartungen vor der Ausfuehrung und schlaegt fehl, wenn diese nicht erfuellt werden. `spy()` zeichnet Interaktionen auf und erlaubt Assertions nach der Ausfuehrung, ohne vorab Erwartungen festzulegen. Die Wahl haengt davon ab, ob der Test Verhalten verifizieren (mock), Interaktionen aufzeichnen (spy) oder Seiteneffekte verhindern (fake) soll. **Wie unterscheidet sich `RefreshDatabase` von `DatabaseTransactions`?** `RefreshDatabase` fuehrt Migrationen einmal aus und kapselt jeden Test in eine Transaktion. `DatabaseTransactions` geht davon aus, dass die Datenbank bereits das korrekte Schema hat, und kapselt die Tests nur in Transaktionen. `RefreshDatabase` ist sicherer fuer CI-Pipelines, in denen die Datenbank moeglicherweise noch nicht existiert. `DatabaseTransactions` ist schneller, wenn das Schema garantiert aktuell ist. **Wann sollten Feature Tests gegenueber Unit Tests bevorzugt werden?** Feature Tests sollten jeden Code abdecken, der mit der HTTP-Schicht, der Datenbank oder Laravel-Services interagiert. Unit Tests sind fuer reine Funktionen und Value Objects ohne Framework-Abhaengigkeiten reserviert. In einer typischen Laravel-Anwendung ueberwiegen Feature Tests gegenueber Unit Tests im Verhaeltnis von etwa 3:1 oder hoeher. Dies spiegelt die Realitaet wider, dass der Grossteil des Laravel-Codes inherent mit dem Framework integriert ist. **Wie verbessert Mutation Testing die Testqualitaet ueber Code Coverage hinaus?** Code Coverage misst Ausfuehrungspfade. Ein Test kann 100% Coverage erreichen, indem er jede Methode aufruft, ohne etwas Aussagekraeftiges zu pruefen. Mutation Testing veraendert den Quellcode (aendert Operatoren, entfernt Returns, kehrt Booleans um) und verifiziert, dass mindestens ein Test fehlschlaegt. Ueberlebende Mutationen zeigen Assertions auf, die zu locker oder gaenzlich fehlend sind. `php artisan test --mutate` erzeugt einen Mutation Score in Prozent neben der Standard-Coverage. Fuer weitere [Laravel Interviewfragen](/blog/laravel/laravel-php-interview-questions) deckt die SharpSkill-Fragendatenbank Authentifizierung, [Service Container Patterns](/technologies/laravel/interview-questions/service-container-di), Eloquent-Beziehungen und Queue-Architektur ab. ## HTTP-Response-Tests und JSON-Assertions Laravels HTTP-Testing-Helper verifizieren Statuscodes, Header, JSON-Strukturen und Redirect-Ziele. In Kombination mit Pest entstehen kompakte Integrationstests. ```php // tests/Feature/ApiAuthenticationTest.php use App\Models\User; describe('API Authentication', function () { it('rejects unauthenticated requests with 401', function () { ->getJson('/api/profile') ->assertUnauthorized(); }); it('returns the authenticated user profile', function () { = User::factory()->create([ 'name' => 'John Doe', 'email' => 'john@example.com', ]); ->actingAs() ->getJson('/api/profile') ->assertOk() ->assertJson([ 'data' => [ 'name' => 'John Doe', 'email' => 'john@example.com', ], ]); }); it('validates required fields on registration', function () { ->postJson('/api/register', []) ->assertUnprocessable() ->assertJsonValidationErrors(['name', 'email', 'password']); }); }); ``` Der `describe` Block gruppiert authentifizierungsbezogene Tests. Jeder Testname beschreibt das erwartete Verhalten, nicht die Implementierung. Die Methode `assertJsonValidationErrors` prueft, ob bestimmte Felder Validierungsfehlermeldungen enthalten. ## Fazit - Pest 4 mit Laravel 12 eliminiert Boilerplate durch eine fluessige Konfigurations-API, `expect()` Verkettungen und automatische Testerkennung - Feature Tests sollten den Kern einer Laravel Test Suite bilden, waehrend Unit Tests fuer isolierte Geschaeftslogik reserviert bleiben - Facade Fakes (`Mail::fake()`, `Queue::fake()`) uebernehmen das Mocking auf Framework-Ebene, waehrend Mockery Drittanbieter-Abhaengigkeiten handhabt, die ueber den Container injiziert werden - Architektur-Tests setzen strukturelle Regeln durch (kein Eloquent in Controllern, keine Debug-Funktionen) ohne Laufzeit-Overhead - Mutation Testing mit `--mutate` erkennt schwache Assertions, die Code Coverage allein uebersieht - Factory States und verkettete `expect()->and()` Assertions halten den Testdatenaufbau lesbar und die Assertions spezifisch - Fuer die [Laravel-Interviewvorbereitung](/technologies/laravel) demonstriert das Verstaendnis der Mock/Fake/Spy-Unterscheidung, des `RefreshDatabase`-Verhaltens und des Mutation Testings eine Testing-Reife jenseits einfacher Coverage --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/de/blog/laravel/laravel-testing-pest-mocking-best-practices