Mastering Test Doubles and Fakes in Laravel: A Comprehensive Guide

Laravel////4 min read

Beyond the Vocabulary: Understanding Test Doubles

Testing often feels like a steep mountain to climb because of the dense academic language surrounding it. simplifies this by breaking down the five essential types of test doubles defined by . Understanding these is the first step toward writing cleaner tests in .

  • Dummies: These are placeholders. You pass them into functions to satisfy parameter lists, but you never actually use their values.
  • Fakes: These have real working implementations but take shortcuts. A classic example is using an in-memory database instead of a full instance.
  • Stubs: These provide "canned answers." They respond to specific calls with pre-defined data but don't care about anything else.
  • Spies: Think of these as stubs that take notes. They record what happened so you can verify it later.
  • Mocks: The strictest form. They are pre-programmed with expectations and will actively throw an exception if they receive a call they didn't expect.

The Power of Laravel Facade Fakes

For years, facades faced criticism for being "untestable" because of their static nature. This hasn't been true for a decade. provides built-in fake implementations for almost every major service, including , , and .

When you call Mail::fake(), you aren't just ignoring emails. You are swapping the real mailer in the service container with a MailFake instance. This fake records every mailable sent, allowing you to make powerful assertions without actually hitting an SMTP server.

public function test_post_store_sends_welcome_email()
{
    Mail::fake();

    // Perform the action
    $this->post('/posts', ['title' => 'New Post']);

    // Assert the mailable was sent to the right person
    Mail::assertQueued(NewPostMailable::class, function ($mail) {
        return $mail->hasTo('[email protected]');
    });
}

Preventing the "Stray Request" Foot-Gun

One of the most dangerous aspects of testing is the "stray request." This happens when your test accidentally hits a real production API or sends a real email because you forgot to fake a specific service. highlights a brilliant feature in the client: preventStrayRequests().

By adding Http::preventStrayRequests() to your base test case's setUp method, the framework will throw an exception the moment any code attempts to make an external request that hasn't been explicitly faked. This turns your fake into a mock, providing a "tracer bullet" that illuminates hidden dependencies in your code. It’s a best practice that ensures your tests stay fast, deterministic, and isolated from the outside world.

Advanced Techniques: Real-Time Facades and Mockery

Sometimes you need to test a custom service class that doesn't have a built-in fake. You have two primary paths: traditional dependency injection mocking or the "real-time facade."

Using the Mock Helper

Laravel's mock() helper streamlines the process of creating a object and binding it into the container. It’s much cleaner than manual syntax.

$this->mock(LanguageAI::class, function ($mock) {
    $mock->shouldReceive('analyze')->once()->andReturn(true);
});

The Real-Time Facade Magic

If you prefer the clean, static syntax of facades but don't want to create a dedicated Facade class, you can use a real-time facade. By simply prefixing your import with Facades\, generates a facade on the fly.

use Facades\App\Services\LanguageAI;

// In your code or test:
LanguageAI::expects('analyze')
    ->with('post body')
    ->andReturn(true);

Critical Distinction: ShouldReceive vs. Expects

A common mistake is using shouldReceive() when you actually mean expects(). The difference is vital for test integrity.

  • shouldReceive() is permissive. If the method is never called, the test still passes. This leads to "false confidence"—you think you're testing a feature that isn't actually running.
  • expects() is strict. It acts as a true mock. If the method isn't called exactly as specified, the test fails.

Always lean toward expects() to ensure your code is actually executing the logic you intend to verify. Being strict in your tests leads to total confidence in your production deployments.

Topic DensityMention share of the most discussed topics · 16 mentions across 10 distinct topics
31%· products
13%· people
13%· products
6%· products
6%· products
Other topics
31%
End of Article
Source video
Mastering Test Doubles and Fakes in Laravel: A Comprehensive Guide

Jason McCreary "Testing With Fakes " - Laracon US 2023 Nashville

Watch

Laravel // 31:20

The official YouTube channel of Laravel, the clean stack for Artisans and agents. We will update you on what's new in the world of Laravel, from the framework to our products Cloud, Forge, and Nightwatch.

Who and what they mention most
4 min read0%
4 min read