Laravel Testing in 2026: Pest, Mocking en Vragen voor Technische Interviews
Beheers de best practices voor Laravel testing met Pest 4, Mockery, facade fakes en architectuurtests. Behandelt unit tests, feature tests, mocking-strategieën en veelgestelde interviewvragen voor Laravel-ontwikkelaars in 2026.

De best practices voor Laravel testing zijn flink verschoven met Pest 4, mutatietesten en architectuurpresets. Laravel 12 levert eersteklas Pest-ondersteuning, waardoor de testervaring expressiever en minder uitgebreid wordt dan kale PHPUnit. Deze gids behandelt de patronen die ertoe doen voor zowel productietoepassingen als technische interviews.
Pest 4.7 (gebouwd op PHPUnit 12) is het standaard testframework voor Laravel 12. Het vereist PHP 8.3+ en introduceert mutatietesten, time-balanced sharding en architectuurpresets standaard.
Pest 4 Opzetten in een Laravel 12-project
Elke nieuwe Laravel 12-applicatie genereert Pest automatisch. Voor bestaande projecten duurt de migratie van PHPUnit of Pest 3 een paar minuten. De configuratie staat in tests/Pest.php, waar globale traits en helper-methoden worden geregistreerd.
use Illuminate\Foundation\Testing\RefreshDatabase;
pest()
->extend(Tests\TestCase::class)
->use(RefreshDatabase::class)
->in('Feature');Dit ene bestand vervangt de oude CreatesApplication trait en de overerving van de basistestklasse. De RefreshDatabase trait verpakt elke test in een databasetransactie en draait de wijzigingen automatisch terug.
use App\Models\User;
it('registers a new user with valid data', function () {
$response = $this->postJson('/api/register', [
'name' => 'Jane Doe',
'email' => 'jane@example.com',
'password' => 'SecurePass123!',
'password_confirmation' => 'SecurePass123!',
]);
$response->assertStatus(201)
->assertJsonStructure(['user' => ['id', 'name', 'email']]);
expect(User::where('email', 'jane@example.com')->exists())->toBeTrue();
});De expect()-API van Pest schakelt natuurlijk in elkaar met PHPUnit-assertions. De assertJsonStructure-aanroep valideert de vorm van de response, terwijl expect()->toBeTrue() de databasestatus bevestigt. Beide stijlen bestaan zonder conflict naast elkaar.
Unit Tests vs Feature Tests in Laravel
Het onderscheid tussen unit- en feature-tests in Laravel bepaalt welke delen van het framework worden opgestart. Unit tests draaien zonder de applicatiecontainer, wat ze sneller maakt maar beperkt tot pure logica. Feature tests starten de volledige applicatie op, wat HTTP-aanroepen, databasequery's en service-resolutie mogelijk maakt.
use App\Services\PriceCalculator;
describe('PriceCalculator', function () {
it('applies a percentage discount correctly', function () {
$calculator = new PriceCalculator();
// 20% off a 150.00 base price
$result = $calculator->applyDiscount(150.00, 20);
expect($result)->toBe(120.00);
});
it('rejects negative discount values', function () {
$calculator = new PriceCalculator();
expect(fn () => $calculator->applyDiscount(100.00, -5))
->toThrow(InvalidArgumentException::class);
});
});Unit tests richten zich op geïsoleerde klassen zonder externe afhankelijkheden. Het describe-blok groepeert gerelateerde assertions, en Pest 4 ondersteunt geneste describe-blokken voor complexe testhiërarchieën.
Feature tests zouden het merendeel van een Laravel-testsuite moeten vormen. Ze vangen integratiebugs op die unit tests missen, zoals onjuiste route-middleware, ontbrekende validatieregels of kapotte Eloquent-relaties. Een veelgebruikte vuistregel: als de code de database, de HTTP-laag of een facade raakt, schrijf dan een feature test.
Mocking-strategieën met Facades en Mockery
Mocking in Laravel testing isoleert de code die wordt getest van externe afhankelijkheden. Laravel-facades bieden ingebouwde fake-implementaties voor queues, events, notificaties, mail en storage. Mockery regelt al het overige.
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 = User::factory()->create();
$order = Order::factory()->for($user)->create([
'total' => 99.99,
'status' => 'pending',
]);
// Act: confirm the order via HTTP
$this->actingAs($user)
->postJson("/api/orders/{$order->id}/confirm")
->assertOk();
// Assert: payment job was dispatched with correct amount
Queue::assertPushed(ProcessPayment::class, function ($job) use ($order) {
return $job->order->id === $order->id
&& $job->order->total === 99.99;
});
// Assert: confirmation email was sent to the user
Mail::assertSent(OrderConfirmation::class, function ($mail) use ($user) {
return $mail->hasTo($user->email);
});
});Facade fakes onderscheppen aanroepen op frameworkniveau, zodat er geen echte e-mails worden verstuurd of jobs worden verzonden. De op closures gebaseerde assertions verifiëren de exacte data die aan elke component wordt doorgegeven.
Vermijd het mocken van interne domeinklassen. Mock op de grens: third-party API's, mail, queues, bestandssystemen. Te veel mocken maakt tests broos en sterk gekoppeld aan implementatiedetails.
Voor afhankelijkheden die geen facades zijn, injecteer ze via de constructor en gebruik Mockery:
use App\Services\PaymentGateway;
use App\Services\StripeClient;
it('charges the customer through the payment gateway', function () {
// Create a mock of the Stripe client
$stripeClient = Mockery::mock(StripeClient::class);
$stripeClient->shouldReceive('charge')
->once()
->with('cus_abc123', 5000, 'usd')
->andReturn(['status' => 'succeeded', 'id' => 'ch_xyz']);
// Bind the mock in the container
$this->app->instance(StripeClient::class, $stripeClient);
$gateway = app(PaymentGateway::class);
$result = $gateway->processCharge('cus_abc123', 50.00);
expect($result['status'])->toBe('succeeded');
});Het binden van de mock met $this->app->instance() vervangt de echte implementatie voor de duur van de test. Mockery verifieert dat charge precies één keer is aangeroepen met de verwachte argumenten.
Architectuurtesten met Pest-presets
Pest 4 bevat architectuurtesten die structurele regels in de hele codebase afdwingen. Deze tests draaien tegen de AST, niet tegen de runtime, waardoor ze extreem snel zijn.
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();De eerste regel voorkomt dat controllers de database rechtstreeks bevragen en dwingt zo een servicelaag-patroon af. De tweede zorgt ervoor dat services niet uitgebreid kunnen worden, wat de overervingscomplexiteit vermindert. De derde vangt achtergebleven debug-statements op voordat ze de productie bereiken.
Laravel-specifieke presets zijn ook beschikbaar:
arch()->preset()->laravel();
arch()->preset()->security();
arch()->preset()->php();Deze drie regels dwingen tientallen regels af: modellen erven de juiste basisklasse, controllers bevatten geen bedrijfslogica, geen onveilige functies zoals eval() of md5() voor hashing, en standaard PHP-conventies worden gevolgd.
Klaar om je Laravel gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
Mutatietesten om Testkwaliteit te Verifiëren
Code coverage meet welke regels tijdens tests worden uitgevoerd. Mutatietesten gaat verder: het wijzigt de broncode en controleert of de tests de verandering detecteren. Als een mutatie overleeft, heeft de testsuite een gat.
# Run mutation testing on a specific class
php artisan test --mutate --class=App\\Services\\PriceCalculatorPest 4 introduceert mutaties zoals het veranderen van > naar >=, het verwijderen van return-statements en het omkeren van booleaanse condities. Een mutatiescore onder de 80% wijst meestal op tests die uitvoer verifiëren zonder edge cases te controleren.
// Example: this test has high coverage but low mutation score
it('calculates shipping cost', function () {
$cost = calculateShipping(weight: 5.0, zone: 'domestic');
// Only checks that the result is numeric
expect($cost)->toBeFloat();
});
// Improved: catches mutations by asserting the exact value
it('calculates domestic shipping for 5kg package', function () {
$cost = calculateShipping(weight: 5.0, zone: 'domestic');
// Exact assertion catches operator and value mutations
expect($cost)->toBe(12.50);
});De eerste test slaagt zelfs als de berekeningslogica volledig fout is, zolang er maar een float wordt geretourneerd. De tweede test faalt onmiddellijk wanneer een mutatie de formule verandert. Specifieke assertions leveren hogere mutatiescores op.
Databasetestpatronen en Factories
Laravel-modelfactories genereren realistische testdata zonder handmatige array-constructie. Pest 4 in combinatie met factories levert leesbare, onderhoudbare data-setup op.
use App\Models\Article;
use App\Models\User;
it('publishes a draft article and updates the timestamp', function () {
$author = User::factory()->create(['role' => 'editor']);
$article = Article::factory()
->for($author, 'author')
->draft()
->create(['title' => 'Testing Best Practices']);
$this->actingAs($author)
->patchJson("/api/articles/{$article->id}/publish")
->assertOk()
->assertJsonPath('data.status', 'published');
$article->refresh();
expect($article->status)->toBe('published')
->and($article->published_at)->not->toBeNull()
->and($article->published_at->isToday())->toBeTrue();
});De draft() factory-state stelt standaardwaarden in voor ongepubliceerde artikelen. Geketende expect()-assertions met ->and() lezen als natuurlijke taal en falen met beschrijvende meldingen.
Voer php artisan test --parallel uit om tests over meerdere processen uit te voeren. Laravel maakt automatisch aparte testdatabases voor elk proces, wat dataconflicten voorkomt. Combineer dit met de time-balanced sharding van Pest 4 voor optimale CI-prestaties.
Veelgestelde Interviewvragen over Laravel Testing
Technische interviews voor Laravel-functies peilen regelmatig naar testkennis. Hieronder de vragen die het vaakst voorkomen, samen met wat een sterk antwoord behandelt.
Wat is het verschil tussen fake(), mock() en spy() in Laravel testing?
Facade fake() vervangt de hele facade door een in-memory implementatie (Mail::fake, Queue::fake). mock() via Mockery stelt verwachtingen in vóór de uitvoering en faalt als ze niet worden ingelost. spy() registreert interacties en staat assertions toe ná de uitvoering, zonder vooraf verwachtingen in te stellen. De keuze hangt ervan af of de test gedrag moet verifiëren (mock), interacties moet registreren (spy) of neveneffecten moet voorkomen (fake).
Hoe verschilt RefreshDatabase van DatabaseTransactions?
RefreshDatabase voert migraties één keer uit en verpakt elke test in een transactie. DatabaseTransactions gaat ervan uit dat de database al het juiste schema heeft en verpakt tests alleen in transacties. RefreshDatabase is veiliger voor CI-pipelines waar de database mogelijk nog niet bestaat. DatabaseTransactions is sneller wanneer het schema gegarandeerd up-to-date is.
Wanneer verdienen feature tests de voorkeur boven unit tests?
Feature tests zouden alle code moeten dekken die interageert met de HTTP-laag, de database of Laravel-services. Unit tests zijn voorbehouden aan pure functies en waardeobjecten zonder frameworkafhankelijkheden. In een typische Laravel-applicatie overtreffen feature tests de unit tests in een verhouding van ongeveer 3:1 of hoger. Dit weerspiegelt de realiteit dat de meeste Laravel-code inherent geïntegreerd is met het framework.
Hoe verbetert mutatietesten de testkwaliteit verder dan code coverage?
Code coverage meet uitvoeringspaden. Een test kan 100% coverage behalen door elke methode aan te roepen zonder iets zinnigs te asserten. Mutatietesten wijzigt de broncode (operators veranderen, returns verwijderen, booleans omkeren) en verifieert dat ten minste één test faalt. Overlevende mutaties onthullen assertions die te los of volledig afwezig zijn. Het uitvoeren van php artisan test --mutate levert een mutatiescore-percentage op naast de standaard coverage.
Voor meer Laravel-interviewvragen behandelt de SharpSkill-vragenbank authenticatie, service container-patronen, Eloquent-relaties en queue-architectuur.
HTTP-responses en JSON-assertions Testen
De HTTP-testhelpers van Laravel verifiëren statuscodes, headers, JSON-structuren en redirect-doelen. In combinatie met Pest vormen ze beknopte integratietests.
use App\Models\User;
describe('API Authentication', function () {
it('rejects unauthenticated requests with 401', function () {
$this->getJson('/api/profile')
->assertUnauthorized();
});
it('returns the authenticated user profile', function () {
$user = User::factory()->create([
'name' => 'John Doe',
'email' => 'john@example.com',
]);
$this->actingAs($user)
->getJson('/api/profile')
->assertOk()
->assertJson([
'data' => [
'name' => 'John Doe',
'email' => 'john@example.com',
],
]);
});
it('validates required fields on registration', function () {
$this->postJson('/api/register', [])
->assertUnprocessable()
->assertJsonValidationErrors(['name', 'email', 'password']);
});
});Het describe-blok groepeert authenticatiegerelateerde tests. Elke testnaam beschrijft het verwachte gedrag, niet de implementatie. De assertJsonValidationErrors-methode controleert of specifieke velden validatiefoutmeldingen hebben.
Conclusie
- Pest 4 met Laravel 12 elimineert boilerplate via een vloeiende configuratie-API,
expect()-ketens en automatische testdetectie - Feature tests zouden de kern van een Laravel-testsuite moeten vormen, met unit tests voorbehouden aan geïsoleerde bedrijfslogica
- Facade fakes (
Mail::fake(),Queue::fake()) handelen mocking op frameworkniveau af, terwijl Mockery third-party afhankelijkheden afhandelt die via de container worden geïnjecteerd - Architectuurtests dwingen structurele regels af (geen Eloquent in controllers, geen debug-functies) zonder runtime-overhead
- Mutatietesten met
--mutatevangt zwakke assertions op die code coverage alleen mist - Factory-states en geketende
expect()->and()-assertions houden de testdata-setup leesbaar en de assertions specifiek - Voor Laravel-interviewvoorbereiding toont begrip van het onderscheid tussen mock/fake/spy, het gedrag van
RefreshDatabaseen mutatietesten een testvolwassenheid die verder gaat dan basale coverage
Begin met oefenen!
Test je kennis met onze gespreksimulatoren en technische tests.
Tags
Delen
Gerelateerde artikelen

Laravel 12 in 2026: Nieuwe Features, Starter Kits en Sollicitatievragen
Laravel 12 introduceert volledig vernieuwde Starter Kits met React 19, Vue 3, Livewire 4 en WorkOS AuthKit. Een complete gids over nieuwe features, het upgradepad en belangrijke sollicitatievragen voor 2026.

Laravel en PHP sollicitatievragen: de Top 25 in 2026
De 25 meest gestelde Laravel- en PHP-sollicitatievragen. Eloquent ORM, middleware, Artisan, queues, tests en architectuur met uitgebreide antwoorden en codevoorbeelden.

Laravel Middleware Uitgelicht: Authenticatie, Rate Limiting en Eigen Middleware
Uitgebreide handleiding over Laravel middleware met praktische voorbeelden voor authenticatie, rate limiting met throttle, eigen middleware bouwen en geavanceerde patronen voor productieomgevingen.