Mocking

Introduction

When testing Laravel applications, you may wish to "mock" certain aspects of your application so they are not actually executed during a given test. For example, when testing a controller that dispatches an event, you may wish to mock the event listeners so they are not actually executed during the test. This allows you to only test the controller's HTTP response without worrying about the execution of the event listeners since the event listeners can be tested in their own test case.

Laravel provides helpful methods for mocking events, jobs, and other facades out of the box. These helpers primarily provide a convenience layer over Mockery so you do not have to manually make complicated Mockery method calls.

Mocking Objects

When mocking an object that is going to be injected into your application via Laravel's service container, you will need to bind your mocked instance into the container as an instance binding. This will instruct the container to use your mocked instance of the object instead of constructing the object itself:

1use App\Service;
2use Mockery;
3use Mockery\MockInterface;
4 
5public function test_something_can_be_mocked()
6{
7 $this->instance(
8 Service::class,
9 Mockery::mock(Service::class, function (MockInterface $mock) {
10 $mock->shouldReceive('process')->once();
11 })
12 );
13}
1use App\Service;
2use Mockery;
3use Mockery\MockInterface;
4 
5public function test_something_can_be_mocked()
6{
7 $this->instance(
8 Service::class,
9 Mockery::mock(Service::class, function (MockInterface $mock) {
10 $mock->shouldReceive('process')->once();
11 })
12 );
13}

In order to make this more convenient, you may use the mock method that is provided by Laravel's base test case class. For example, the following example is equivalent to the example above:

1use App\Service;
2use Mockery\MockInterface;
3 
4$mock = $this->mock(Service::class, function (MockInterface $mock) {
5 $mock->shouldReceive('process')->once();
6});
1use App\Service;
2use Mockery\MockInterface;
3 
4$mock = $this->mock(Service::class, function (MockInterface $mock) {
5 $mock->shouldReceive('process')->once();
6});

You may use the partialMock method when you only need to mock a few methods of an object. The methods that are not mocked will be executed normally when called:

1use App\Service;
2use Mockery\MockInterface;
3 
4$mock = $this->partialMock(Service::class, function (MockInterface $mock) {
5 $mock->shouldReceive('process')->once();
6});
1use App\Service;
2use Mockery\MockInterface;
3 
4$mock = $this->partialMock(Service::class, function (MockInterface $mock) {
5 $mock->shouldReceive('process')->once();
6});

Similarly, if you want to spy on an object, Laravel's base test case class offers a spy method as a convenient wrapper around the Mockery::spy method. Spies are similar to mocks; however, spies record any interaction between the spy and the code being tested, allowing you to make assertions after the code is executed:

1use App\Service;
2 
3$spy = $this->spy(Service::class);
4 
5// ...
6 
7$spy->shouldHaveReceived('process');
1use App\Service;
2 
3$spy = $this->spy(Service::class);
4 
5// ...
6 
7$spy->shouldHaveReceived('process');

Mocking Facades

Unlike traditional static method calls, facades (including real-time facades) may be mocked. This provides a great advantage over traditional static methods and grants you the same testability that you would have if you were using traditional dependency injection. When testing, you may often want to mock a call to a Laravel facade that occurs in one of your controllers. For example, consider the following controller action:

1<?php
2 
3namespace App\Http\Controllers;
4 
5use Illuminate\Support\Facades\Cache;
6 
7class UserController extends Controller
8{
9 /**
10 * Retrieve a list of all users of the application.
11 *
12 * @return \Illuminate\Http\Response
13 */
14 public function index()
15 {
16 $value = Cache::get('key');
17 
18 //
19 }
20}
1<?php
2 
3namespace App\Http\Controllers;
4 
5use Illuminate\Support\Facades\Cache;
6 
7class UserController extends Controller
8{
9 /**
10 * Retrieve a list of all users of the application.
11 *
12 * @return \Illuminate\Http\Response
13 */
14 public function index()
15 {
16 $value = Cache::get('key');
17 
18 //
19 }
20}

We can mock the call to the Cache facade by using the shouldReceive method, which will return an instance of a Mockery mock. Since facades are actually resolved and managed by the Laravel service container, they have much more testability than a typical static class. For example, let's mock our call to the Cache facade's get method:

1<?php
2 
3namespace Tests\Feature;
4 
5use Illuminate\Foundation\Testing\RefreshDatabase;
6use Illuminate\Foundation\Testing\WithoutMiddleware;
7use Illuminate\Support\Facades\Cache;
8use Tests\TestCase;
9 
10class UserControllerTest extends TestCase
11{
12 public function testGetIndex()
13 {
14 Cache::shouldReceive('get')
15 ->once()
16 ->with('key')
17 ->andReturn('value');
18 
19 $response = $this->get('/users');
20 
21 // ...
22 }
23}
1<?php
2 
3namespace Tests\Feature;
4 
5use Illuminate\Foundation\Testing\RefreshDatabase;
6use Illuminate\Foundation\Testing\WithoutMiddleware;
7use Illuminate\Support\Facades\Cache;
8use Tests\TestCase;
9 
10class UserControllerTest extends TestCase
11{
12 public function testGetIndex()
13 {
14 Cache::shouldReceive('get')
15 ->once()
16 ->with('key')
17 ->andReturn('value');
18 
19 $response = $this->get('/users');
20 
21 // ...
22 }
23}
exclamation

You should not mock the Request facade. Instead, pass the input you desire into the HTTP testing methods such as get and post when running your test. Likewise, instead of mocking the Config facade, call the Config::set method in your tests.

Facade Spies

If you would like to spy on a facade, you may call the spy method on the corresponding facade. Spies are similar to mocks; however, spies record any interaction between the spy and the code being tested, allowing you to make assertions after the code is executed:

1use Illuminate\Support\Facades\Cache;
2 
3public function test_values_are_be_stored_in_cache()
4{
5 Cache::spy();
6 
7 $response = $this->get('/');
8 
9 $response->assertStatus(200);
10 
11 Cache::shouldHaveReceived('put')->once()->with('name', 'Taylor', 10);
12}
1use Illuminate\Support\Facades\Cache;
2 
3public function test_values_are_be_stored_in_cache()
4{
5 Cache::spy();
6 
7 $response = $this->get('/');
8 
9 $response->assertStatus(200);
10 
11 Cache::shouldHaveReceived('put')->once()->with('name', 'Taylor', 10);
12}

Bus Fake

When testing code that dispatches jobs, you typically want to assert that a given job was dispatched but not actually queue or execute the job. This is because the job's execution can normally be tested in a separate test class.

You may use the Bus facade's fake method to prevent jobs from being dispatched to the queue. Then, after executing the code under test, you may inspect which jobs the application attempted to dispatch using the assertDispatched and assertNotDispatched methods:

1<?php
2 
3namespace Tests\Feature;
4 
5use App\Jobs\ShipOrder;
6use Illuminate\Foundation\Testing\RefreshDatabase;
7use Illuminate\Foundation\Testing\WithoutMiddleware;
8use Illuminate\Support\Facades\Bus;
9use Tests\TestCase;
10 
11class ExampleTest extends TestCase
12{
13 public function test_orders_can_be_shipped()
14 {
15 Bus::fake();
16 
17 // Perform order shipping...
18 
19 // Assert that a job was dispatched...
20 Bus::assertDispatched(ShipOrder::class);
21 
22 // Assert a job was not dispatched...
23 Bus::assertNotDispatched(AnotherJob::class);
24 
25 // Assert that a job was dispatched synchronously...
26 Bus::assertDispatchedSync(AnotherJob::class);
27 
28 // Assert that a job was not dispatched synchronously...
29 Bus::assertNotDispatchedSync(AnotherJob::class);
30 
31 // Assert that a job was dispatched after the response was sent...
32 Bus::assertDispatchedAfterResponse(AnotherJob::class);
33 
34 // Assert a job was not dispatched after response was sent...
35 Bus::assertNotDispatchedAfterResponse(AnotherJob::class);
36 
37 // Assert no jobs were dispatched...
38 Bus::assertNothingDispatched();
39 }
40}
1<?php
2 
3namespace Tests\Feature;
4 
5use App\Jobs\ShipOrder;
6use Illuminate\Foundation\Testing\RefreshDatabase;
7use Illuminate\Foundation\Testing\WithoutMiddleware;
8use Illuminate\Support\Facades\Bus;
9use Tests\TestCase;
10 
11class ExampleTest extends TestCase
12{
13 public function test_orders_can_be_shipped()
14 {
15 Bus::fake();
16 
17 // Perform order shipping...
18 
19 // Assert that a job was dispatched...
20 Bus::assertDispatched(ShipOrder::class);
21 
22 // Assert a job was not dispatched...
23 Bus::assertNotDispatched(AnotherJob::class);
24 
25 // Assert that a job was dispatched synchronously...
26 Bus::assertDispatchedSync(AnotherJob::class);
27 
28 // Assert that a job was not dispatched synchronously...
29 Bus::assertNotDispatchedSync(AnotherJob::class);
30 
31 // Assert that a job was dispatched after the response was sent...
32 Bus::assertDispatchedAfterResponse(AnotherJob::class);
33 
34 // Assert a job was not dispatched after response was sent...
35 Bus::assertNotDispatchedAfterResponse(AnotherJob::class);
36 
37 // Assert no jobs were dispatched...
38 Bus::assertNothingDispatched();
39 }
40}

You may pass a closure to the available methods in order to assert that a job was dispatched that passes a given "truth test". If at least one job was dispatched that passes the given truth test then the assertion will be successful. For example, you may wish to assert that a job was dispatched for a specific order:

1Bus::assertDispatched(function (ShipOrder $job) use ($order) {
2 return $job->order->id === $order->id;
3});
1Bus::assertDispatched(function (ShipOrder $job) use ($order) {
2 return $job->order->id === $order->id;
3});

Faking A Subset Of Jobs

If you only want to prevent certain jobs from being dispatched, you may pass the jobs that should be faked to the fake method:

1/**
2 * Test order process.
3 */
4public function test_orders_can_be_shipped()
5{
6 Bus::fake([
7 ShipOrder::class,
8 ]);
9 
10 // ...
11}
1/**
2 * Test order process.
3 */
4public function test_orders_can_be_shipped()
5{
6 Bus::fake([
7 ShipOrder::class,
8 ]);
9 
10 // ...
11}

You may fake all jobs except for a set of specified jobs using the except method:

1Bus::fake()->except([
2 ShipOrder::class,
3]);
1Bus::fake()->except([
2 ShipOrder::class,
3]);

Job Chains

The Bus facade's assertChained method may be used to assert that a chain of jobs was dispatched. The assertChained method accepts an array of chained jobs as its first argument:

1use App\Jobs\RecordShipment;
2use App\Jobs\ShipOrder;
3use App\Jobs\UpdateInventory;
4use Illuminate\Support\Facades\Bus;
5 
6Bus::assertChained([
7 ShipOrder::class,
8 RecordShipment::class,
9 UpdateInventory::class
10]);
1use App\Jobs\RecordShipment;
2use App\Jobs\ShipOrder;
3use App\Jobs\UpdateInventory;
4use Illuminate\Support\Facades\Bus;
5 
6Bus::assertChained([
7 ShipOrder::class,
8 RecordShipment::class,
9 UpdateInventory::class
10]);

As you can see in the example above, the array of chained jobs may be an array of the job's class names. However, you may also provide an array of actual job instances. When doing so, Laravel will ensure that the job instances are of the same class and have the same property values of the chained jobs dispatched by your application:

1Bus::assertChained([
2 new ShipOrder,
3 new RecordShipment,
4 new UpdateInventory,
5]);
1Bus::assertChained([
2 new ShipOrder,
3 new RecordShipment,
4 new UpdateInventory,
5]);

Job Batches

The Bus facade's assertBatched method may be used to assert that a batch of jobs was dispatched. The closure given to the assertBatched method receives an instance of Illuminate\Bus\PendingBatch, which may be used to inspect the jobs within the batch:

1use Illuminate\Bus\PendingBatch;
2use Illuminate\Support\Facades\Bus;
3 
4Bus::assertBatched(function (PendingBatch $batch) {
5 return $batch->name == 'import-csv' &&
6 $batch->jobs->count() === 10;
7});
1use Illuminate\Bus\PendingBatch;
2use Illuminate\Support\Facades\Bus;
3 
4Bus::assertBatched(function (PendingBatch $batch) {
5 return $batch->name == 'import-csv' &&
6 $batch->jobs->count() === 10;
7});

Testing Job / Batch Interaction

In addition, you may occasionally need to test an individual job's interaction with its underlying batch. For example, you may need to test if a job cancelled further processing for its batch. To accomplish this, you need to assign a fake batch to the job via the withFakeBatch method. The withFakeBatch method returns a tuple containing the job instance and the fake batch:

1[$job, $batch] = (new ShipOrder)->withFakeBatch();
2 
3$job->handle();
4 
5$this->assertTrue($batch->cancelled());
6$this->assertEmpty($batch->added);
1[$job, $batch] = (new ShipOrder)->withFakeBatch();
2 
3$job->handle();
4 
5$this->assertTrue($batch->cancelled());
6$this->assertEmpty($batch->added);

Event Fake

When testing code that dispatches events, you may wish to instruct Laravel to not actually execute the event's listeners. Using the Event facade's fake method, you may prevent listeners from executing, execute the code under test, and then assert which events were dispatched by your application using the assertDispatched, assertNotDispatched, and assertNothingDispatched methods:

1<?php
2 
3namespace Tests\Feature;
4 
5use App\Events\OrderFailedToShip;
6use App\Events\OrderShipped;
7use Illuminate\Foundation\Testing\RefreshDatabase;
8use Illuminate\Foundation\Testing\WithoutMiddleware;
9use Illuminate\Support\Facades\Event;
10use Tests\TestCase;
11 
12class ExampleTest extends TestCase
13{
14 /**
15 * Test order shipping.
16 */
17 public function test_orders_can_be_shipped()
18 {
19 Event::fake();
20 
21 // Perform order shipping...
22 
23 // Assert that an event was dispatched...
24 Event::assertDispatched(OrderShipped::class);
25 
26 // Assert an event was dispatched twice...
27 Event::assertDispatched(OrderShipped::class, 2);
28 
29 // Assert an event was not dispatched...
30 Event::assertNotDispatched(OrderFailedToShip::class);
31 
32 // Assert that no events were dispatched...
33 Event::assertNothingDispatched();
34 }
35}
1<?php
2 
3namespace Tests\Feature;
4 
5use App\Events\OrderFailedToShip;
6use App\Events\OrderShipped;
7use Illuminate\Foundation\Testing\RefreshDatabase;
8use Illuminate\Foundation\Testing\WithoutMiddleware;
9use Illuminate\Support\Facades\Event;
10use Tests\TestCase;
11 
12class ExampleTest extends TestCase
13{
14 /**
15 * Test order shipping.
16 */
17 public function test_orders_can_be_shipped()
18 {
19 Event::fake();
20 
21 // Perform order shipping...
22 
23 // Assert that an event was dispatched...
24 Event::assertDispatched(OrderShipped::class);
25 
26 // Assert an event was dispatched twice...
27 Event::assertDispatched(OrderShipped::class, 2);
28 
29 // Assert an event was not dispatched...
30 Event::assertNotDispatched(OrderFailedToShip::class);
31 
32 // Assert that no events were dispatched...
33 Event::assertNothingDispatched();
34 }
35}

You may pass a closure to the assertDispatched or assertNotDispatched methods in order to assert that an event was dispatched that passes a given "truth test". If at least one event was dispatched that passes the given truth test then the assertion will be successful:

1Event::assertDispatched(function (OrderShipped $event) use ($order) {
2 return $event->order->id === $order->id;
3});
1Event::assertDispatched(function (OrderShipped $event) use ($order) {
2 return $event->order->id === $order->id;
3});

If you would simply like to assert that an event listener is listening to a given event, you may use the assertListening method:

1Event::assertListening(
2 OrderShipped::class,
3 SendShipmentNotification::class
4);
1Event::assertListening(
2 OrderShipped::class,
3 SendShipmentNotification::class
4);
exclamation

After calling Event::fake(), no event listeners will be executed. So, if your tests use model factories that rely on events, such as creating a UUID during a model's creating event, you should call Event::fake() after using your factories.

Faking A Subset Of Events

If you only want to fake event listeners for a specific set of events, you may pass them to the fake or fakeFor method:

1/**
2 * Test order process.
3 */
4public function test_orders_can_be_processed()
5{
6 Event::fake([
7 OrderCreated::class,
8 ]);
9 
10 $order = Order::factory()->create();
11 
12 Event::assertDispatched(OrderCreated::class);
13 
14 // Other events are dispatched as normal...
15 $order->update([...]);
16}
1/**
2 * Test order process.
3 */
4public function test_orders_can_be_processed()
5{
6 Event::fake([
7 OrderCreated::class,
8 ]);
9 
10 $order = Order::factory()->create();
11 
12 Event::assertDispatched(OrderCreated::class);
13 
14 // Other events are dispatched as normal...
15 $order->update([...]);
16}

You may fake all events except for a set of specified events using the except method:

1Event::fake()->except([
2 OrderCreated::class,
3]);
1Event::fake()->except([
2 OrderCreated::class,
3]);

Scoped Event Fakes

If you only want to fake event listeners for a portion of your test, you may use the fakeFor method:

1<?php
2 
3namespace Tests\Feature;
4 
5use App\Events\OrderCreated;
6use App\Models\Order;
7use Illuminate\Foundation\Testing\RefreshDatabase;
8use Illuminate\Support\Facades\Event;
9use Illuminate\Foundation\Testing\WithoutMiddleware;
10use Tests\TestCase;
11 
12class ExampleTest extends TestCase
13{
14 /**
15 * Test order process.
16 */
17 public function test_orders_can_be_processed()
18 {
19 $order = Event::fakeFor(function () {
20 $order = Order::factory()->create();
21 
22 Event::assertDispatched(OrderCreated::class);
23 
24 return $order;
25 });
26 
27 // Events are dispatched as normal and observers will run ...
28 $order->update([...]);
29 }
30}
1<?php
2 
3namespace Tests\Feature;
4 
5use App\Events\OrderCreated;
6use App\Models\Order;
7use Illuminate\Foundation\Testing\RefreshDatabase;
8use Illuminate\Support\Facades\Event;
9use Illuminate\Foundation\Testing\WithoutMiddleware;
10use Tests\TestCase;
11 
12class ExampleTest extends TestCase
13{
14 /**
15 * Test order process.
16 */
17 public function test_orders_can_be_processed()
18 {
19 $order = Event::fakeFor(function () {
20 $order = Order::factory()->create();
21 
22 Event::assertDispatched(OrderCreated::class);
23 
24 return $order;
25 });
26 
27 // Events are dispatched as normal and observers will run ...
28 $order->update([...]);
29 }
30}

HTTP Fake

The Http facade's fake method allows you to instruct the HTTP client to return stubbed / dummy responses when requests are made. For more information on faking outgoing HTTP requests, please consult the HTTP Client testing documentation.

Mail Fake

You may use the Mail facade's fake method to prevent mail from being sent. Typically, sending mail is unrelated to the code you are actually testing. Most likely, it is sufficient to simply assert that Laravel was instructed to send a given mailable.

After calling the Mail facade's fake method, you may then assert that mailables were instructed to be sent to users and even inspect the data the mailables received:

1<?php
2 
3namespace Tests\Feature;
4 
5use App\Mail\OrderShipped;
6use Illuminate\Foundation\Testing\RefreshDatabase;
7use Illuminate\Foundation\Testing\WithoutMiddleware;
8use Illuminate\Support\Facades\Mail;
9use Tests\TestCase;
10 
11class ExampleTest extends TestCase
12{
13 public function test_orders_can_be_shipped()
14 {
15 Mail::fake();
16 
17 // Perform order shipping...
18 
19 // Assert that no mailables were sent...
20 Mail::assertNothingSent();
21 
22 // Assert that a mailable was sent...
23 Mail::assertSent(OrderShipped::class);
24 
25 // Assert a mailable was sent twice...
26 Mail::assertSent(OrderShipped::class, 2);
27 
28 // Assert a mailable was not sent...
29 Mail::assertNotSent(AnotherMailable::class);
30 }
31}
1<?php
2 
3namespace Tests\Feature;
4 
5use App\Mail\OrderShipped;
6use Illuminate\Foundation\Testing\RefreshDatabase;
7use Illuminate\Foundation\Testing\WithoutMiddleware;
8use Illuminate\Support\Facades\Mail;
9use Tests\TestCase;
10 
11class ExampleTest extends TestCase
12{
13 public function test_orders_can_be_shipped()
14 {
15 Mail::fake();
16 
17 // Perform order shipping...
18 
19 // Assert that no mailables were sent...
20 Mail::assertNothingSent();
21 
22 // Assert that a mailable was sent...
23 Mail::assertSent(OrderShipped::class);
24 
25 // Assert a mailable was sent twice...
26 Mail::assertSent(OrderShipped::class, 2);
27 
28 // Assert a mailable was not sent...
29 Mail::assertNotSent(AnotherMailable::class);
30 }
31}

If you are queueing mailables for delivery in the background, you should use the assertQueued method instead of assertSent:

1Mail::assertQueued(OrderShipped::class);
2 
3Mail::assertNotQueued(OrderShipped::class);
4 
5Mail::assertNothingQueued();
1Mail::assertQueued(OrderShipped::class);
2 
3Mail::assertNotQueued(OrderShipped::class);
4 
5Mail::assertNothingQueued();

You may pass a closure to the assertSent, assertNotSent, assertQueued, or assertNotQueued methods in order to assert that a mailable was sent that passes a given "truth test". If at least one mailable was sent that passes the given truth test then the assertion will be successful:

1Mail::assertSent(function (OrderShipped $mail) use ($order) {
2 return $mail->order->id === $order->id;
3});
1Mail::assertSent(function (OrderShipped $mail) use ($order) {
2 return $mail->order->id === $order->id;
3});

When calling the Mail facade's assertion methods, the mailable instance accepted by the provided closure exposes helpful methods for examining the mailable:

1Mail::assertSent(OrderShipped::class, function ($mail) use ($user) {
2 return $mail->hasTo($user->email) &&
3 $mail->hasCc('...') &&
4 $mail->hasBcc('...') &&
5 $mail->hasReplyTo('...') &&
6 $mail->hasFrom('...') &&
7 $mail->hasSubject('...');
8});
1Mail::assertSent(OrderShipped::class, function ($mail) use ($user) {
2 return $mail->hasTo($user->email) &&
3 $mail->hasCc('...') &&
4 $mail->hasBcc('...') &&
5 $mail->hasReplyTo('...') &&
6 $mail->hasFrom('...') &&
7 $mail->hasSubject('...');
8});

The mailable instance also includes several helpful methods for examining the attachments on a mailable:

1use Illuminate\Mail\Mailables\Attachment;
2 
3Mail::assertSent(OrderShipped::class, function ($mail) {
4 return $mail->hasAttachment(
5 Attachment::fromPath('/path/to/file')
6 ->as('name.pdf')
7 ->withMime('application/pdf')
8 );
9});
10 
11Mail::assertSent(OrderShipped::class, function ($mail) {
12 return $mail->hasAttachment(
13 Attachment::fromStorageDisk('s3', '/path/to/file')
14 );
15});
16 
17Mail::assertSent(OrderShipped::class, function ($mail) use ($pdfData) {
18 return $mail->hasAttachment(
19 Attachment::fromData(fn () => $pdfData, 'name.pdf')
20 );
21});
1use Illuminate\Mail\Mailables\Attachment;
2 
3Mail::assertSent(OrderShipped::class, function ($mail) {
4 return $mail->hasAttachment(
5 Attachment::fromPath('/path/to/file')
6 ->as('name.pdf')
7 ->withMime('application/pdf')
8 );
9});
10 
11Mail::assertSent(OrderShipped::class, function ($mail) {
12 return $mail->hasAttachment(
13 Attachment::fromStorageDisk('s3', '/path/to/file')
14 );
15});
16 
17Mail::assertSent(OrderShipped::class, function ($mail) use ($pdfData) {
18 return $mail->hasAttachment(
19 Attachment::fromData(fn () => $pdfData, 'name.pdf')
20 );
21});

You may have noticed that there are two methods for asserting that mail was not sent: assertNotSent and assertNotQueued. Sometimes you may wish to assert that no mail was sent or queued. To accomplish this, you may use the assertNothingOutgoing and assertNotOutgoing methods:

1Mail::assertNothingOutgoing();
2 
3Mail::assertNotOutgoing(function (OrderShipped $mail) use ($order) {
4 return $mail->order->id === $order->id;
5});
1Mail::assertNothingOutgoing();
2 
3Mail::assertNotOutgoing(function (OrderShipped $mail) use ($order) {
4 return $mail->order->id === $order->id;
5});

Testing Mailable Content

We suggest testing the content of your mailables separately from your tests that assert that a given mailable was "sent" to a specific user. To learn how to test the content of your mailables, check out our documentation on the testing mailables.

Notification Fake

You may use the Notification facade's fake method to prevent notifications from being sent. Typically, sending notifications is unrelated to the code you are actually testing. Most likely, it is sufficient to simply assert that Laravel was instructed to send a given notification.

After calling the Notification facade's fake method, you may then assert that notifications were instructed to be sent to users and even inspect the data the notifications received:

1<?php
2 
3namespace Tests\Feature;
4 
5use App\Notifications\OrderShipped;
6use Illuminate\Foundation\Testing\RefreshDatabase;
7use Illuminate\Foundation\Testing\WithoutMiddleware;
8use Illuminate\Support\Facades\Notification;
9use Tests\TestCase;
10 
11class ExampleTest extends TestCase
12{
13 public function test_orders_can_be_shipped()
14 {
15 Notification::fake();
16 
17 // Perform order shipping...
18 
19 // Assert that no notifications were sent...
20 Notification::assertNothingSent();
21 
22 // Assert a notification was sent to the given users...
23 Notification::assertSentTo(
24 [$user], OrderShipped::class
25 );
26 
27 // Assert a notification was not sent...
28 Notification::assertNotSentTo(
29 [$user], AnotherNotification::class
30 );
31 
32 // Assert that a given number of notifications were sent...
33 Notification::assertCount(3);
34 }
35}
1<?php
2 
3namespace Tests\Feature;
4 
5use App\Notifications\OrderShipped;
6use Illuminate\Foundation\Testing\RefreshDatabase;
7use Illuminate\Foundation\Testing\WithoutMiddleware;
8use Illuminate\Support\Facades\Notification;
9use Tests\TestCase;
10 
11class ExampleTest extends TestCase
12{
13 public function test_orders_can_be_shipped()
14 {
15 Notification::fake();
16 
17 // Perform order shipping...
18 
19 // Assert that no notifications were sent...
20 Notification::assertNothingSent();
21 
22 // Assert a notification was sent to the given users...
23 Notification::assertSentTo(
24 [$user], OrderShipped::class
25 );
26 
27 // Assert a notification was not sent...
28 Notification::assertNotSentTo(
29 [$user], AnotherNotification::class
30 );
31 
32 // Assert that a given number of notifications were sent...
33 Notification::assertCount(3);
34 }
35}

You may pass a closure to the assertSentTo or assertNotSentTo methods in order to assert that a notification was sent that passes a given "truth test". If at least one notification was sent that passes the given truth test then the assertion will be successful:

1Notification::assertSentTo(
2 $user,
3 function (OrderShipped $notification, $channels) use ($order) {
4 return $notification->order->id === $order->id;
5 }
6);
1Notification::assertSentTo(
2 $user,
3 function (OrderShipped $notification, $channels) use ($order) {
4 return $notification->order->id === $order->id;
5 }
6);

On-Demand Notifications

If the code you are testing sends on-demand notifications, you can test that the on-demand notification was sent via the assertSentOnDemand method:

1Notification::assertSentOnDemand(OrderShipped::class);
1Notification::assertSentOnDemand(OrderShipped::class);

By passing a closure as the second argument to the assertSentOnDemand method, you may determine if an on-demand notification was sent to the correct "route" address:

1Notification::assertSentOnDemand(
2 OrderShipped::class,
3 function ($notification, $channels, $notifiable) use ($user) {
4 return $notifiable->routes['mail'] === $user->email;
5 }
6);
1Notification::assertSentOnDemand(
2 OrderShipped::class,
3 function ($notification, $channels, $notifiable) use ($user) {
4 return $notifiable->routes['mail'] === $user->email;
5 }
6);

Queue Fake

You may use the Queue facade's fake method to prevent queued jobs from being pushed to the queue. Most likely, it is sufficient to simply assert that Laravel was instructed to push a given job to the queue since the queued jobs themselves may be tested in another test class.

After calling the Queue facade's fake method, you may then assert that the application attempted to push jobs to the queue:

1<?php
2 
3namespace Tests\Feature;
4 
5use App\Jobs\AnotherJob;
6use App\Jobs\FinalJob;
7use App\Jobs\ShipOrder;
8use Illuminate\Foundation\Testing\RefreshDatabase;
9use Illuminate\Foundation\Testing\WithoutMiddleware;
10use Illuminate\Support\Facades\Queue;
11use Tests\TestCase;
12 
13class ExampleTest extends TestCase
14{
15 public function test_orders_can_be_shipped()
16 {
17 Queue::fake();
18 
19 // Perform order shipping...
20 
21 // Assert that no jobs were pushed...
22 Queue::assertNothingPushed();
23 
24 // Assert a job was pushed to a given queue...
25 Queue::assertPushedOn('queue-name', ShipOrder::class);
26 
27 // Assert a job was pushed twice...
28 Queue::assertPushed(ShipOrder::class, 2);
29 
30 // Assert a job was not pushed...
31 Queue::assertNotPushed(AnotherJob::class);
32 }
33}
1<?php
2 
3namespace Tests\Feature;
4 
5use App\Jobs\AnotherJob;
6use App\Jobs\FinalJob;
7use App\Jobs\ShipOrder;
8use Illuminate\Foundation\Testing\RefreshDatabase;
9use Illuminate\Foundation\Testing\WithoutMiddleware;
10use Illuminate\Support\Facades\Queue;
11use Tests\TestCase;
12 
13class ExampleTest extends TestCase
14{
15 public function test_orders_can_be_shipped()
16 {
17 Queue::fake();
18 
19 // Perform order shipping...
20 
21 // Assert that no jobs were pushed...
22 Queue::assertNothingPushed();
23 
24 // Assert a job was pushed to a given queue...
25 Queue::assertPushedOn('queue-name', ShipOrder::class);
26 
27 // Assert a job was pushed twice...
28 Queue::assertPushed(ShipOrder::class, 2);
29 
30 // Assert a job was not pushed...
31 Queue::assertNotPushed(AnotherJob::class);
32 }
33}

You may pass a closure to the assertPushed or assertNotPushed methods in order to assert that a job was pushed that passes a given "truth test". If at least one job was pushed that passes the given truth test then the assertion will be successful:

1Queue::assertPushed(function (ShipOrder $job) use ($order) {
2 return $job->order->id === $order->id;
3});
1Queue::assertPushed(function (ShipOrder $job) use ($order) {
2 return $job->order->id === $order->id;
3});

If you only need to fake specific jobs while allowing your other jobs to execute normally, you may pass the class names of the jobs that should be faked to the fake method:

1public function test_orders_can_be_shipped()
2{
3 Queue::fake([
4 ShipOrder::class,
5 ]);
6 
7 // Perform order shipping...
8 
9 // Assert a job was pushed twice...
10 Queue::assertPushed(ShipOrder::class, 2);
11}
1public function test_orders_can_be_shipped()
2{
3 Queue::fake([
4 ShipOrder::class,
5 ]);
6 
7 // Perform order shipping...
8 
9 // Assert a job was pushed twice...
10 Queue::assertPushed(ShipOrder::class, 2);
11}

Job Chains

The Queue facade's assertPushedWithChain and assertPushedWithoutChain methods may be used to inspect the job chain of a pushed job. The assertPushedWithChain method accepts the primary job as its first argument and an array of chained jobs as its second argument:

1use App\Jobs\RecordShipment;
2use App\Jobs\ShipOrder;
3use App\Jobs\UpdateInventory;
4use Illuminate\Support\Facades\Queue;
5 
6Queue::assertPushedWithChain(ShipOrder::class, [
7 RecordShipment::class,
8 UpdateInventory::class
9]);
1use App\Jobs\RecordShipment;
2use App\Jobs\ShipOrder;
3use App\Jobs\UpdateInventory;
4use Illuminate\Support\Facades\Queue;
5 
6Queue::assertPushedWithChain(ShipOrder::class, [
7 RecordShipment::class,
8 UpdateInventory::class
9]);

As you can see in the example above, the array of chained jobs may be an array of the job's class names. However, you may also provide an array of actual job instances. When doing so, Laravel will ensure that the job instances are of the same class and have the same property values of the chained jobs dispatched by your application:

1Queue::assertPushedWithChain(ShipOrder::class, [
2 new RecordShipment,
3 new UpdateInventory,
4]);
1Queue::assertPushedWithChain(ShipOrder::class, [
2 new RecordShipment,
3 new UpdateInventory,
4]);

You may use the assertPushedWithoutChain method to assert that a job was pushed without a chain of jobs:

1Queue::assertPushedWithoutChain(ShipOrder::class);
1Queue::assertPushedWithoutChain(ShipOrder::class);

Storage Fake

The Storage facade's fake method allows you to easily generate a fake disk that, combined with the file generation utilities of the Illuminate\Http\UploadedFile class, greatly simplifies the testing of file uploads. For example:

1<?php
2 
3namespace Tests\Feature;
4 
5use Illuminate\Foundation\Testing\RefreshDatabase;
6use Illuminate\Foundation\Testing\WithoutMiddleware;
7use Illuminate\Http\UploadedFile;
8use Illuminate\Support\Facades\Storage;
9use Tests\TestCase;
10 
11class ExampleTest extends TestCase
12{
13 public function test_albums_can_be_uploaded()
14 {
15 Storage::fake('photos');
16 
17 $response = $this->json('POST', '/photos', [
18 UploadedFile::fake()->image('photo1.jpg'),
19 UploadedFile::fake()->image('photo2.jpg')
20 ]);
21 
22 // Assert one or more files were stored...
23 Storage::disk('photos')->assertExists('photo1.jpg');
24 Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']);
25 
26 // Assert one or more files were not stored...
27 Storage::disk('photos')->assertMissing('missing.jpg');
28 Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']);
29 
30 // Assert that a given directory is empty...
31 Storage::disk('photos')->assertDirectoryEmpty('/wallpapers');
32 }
33}
1<?php
2 
3namespace Tests\Feature;
4 
5use Illuminate\Foundation\Testing\RefreshDatabase;
6use Illuminate\Foundation\Testing\WithoutMiddleware;
7use Illuminate\Http\UploadedFile;
8use Illuminate\Support\Facades\Storage;
9use Tests\TestCase;
10 
11class ExampleTest extends TestCase
12{
13 public function test_albums_can_be_uploaded()
14 {
15 Storage::fake('photos');
16 
17 $response = $this->json('POST', '/photos', [
18 UploadedFile::fake()->image('photo1.jpg'),
19 UploadedFile::fake()->image('photo2.jpg')
20 ]);
21 
22 // Assert one or more files were stored...
23 Storage::disk('photos')->assertExists('photo1.jpg');
24 Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']);
25 
26 // Assert one or more files were not stored...
27 Storage::disk('photos')->assertMissing('missing.jpg');
28 Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']);
29 
30 // Assert that a given directory is empty...
31 Storage::disk('photos')->assertDirectoryEmpty('/wallpapers');
32 }
33}

By default, the fake method will delete all files in its temporary directory. If you would like to keep these files, you may use the "persistentFake" method instead. For more information on testing file uploads, you may consult the HTTP testing documentation's information on file uploads.

exclamation

The image method requires the GD extension.

Interacting With Time

When testing, you may occasionally need to modify the time returned by helpers such as now or Illuminate\Support\Carbon::now(). Thankfully, Laravel's base feature test class includes helpers that allow you to manipulate the current time:

1use Illuminate\Support\Carbon;
2 
3public function testTimeCanBeManipulated()
4{
5 // Travel into the future...
6 $this->travel(5)->milliseconds();
7 $this->travel(5)->seconds();
8 $this->travel(5)->minutes();
9 $this->travel(5)->hours();
10 $this->travel(5)->days();
11 $this->travel(5)->weeks();
12 $this->travel(5)->years();
13 
14 // Freeze time and resume normal time after executing closure...
15 $this->freezeTime(function (Carbon $time) {
16 // ...
17 });
18 
19 // Travel into the past...
20 $this->travel(-5)->hours();
21 
22 // Travel to an explicit time...
23 $this->travelTo(now()->subHours(6));
24 
25 // Return back to the present time...
26 $this->travelBack();
27}
1use Illuminate\Support\Carbon;
2 
3public function testTimeCanBeManipulated()
4{
5 // Travel into the future...
6 $this->travel(5)->milliseconds();
7 $this->travel(5)->seconds();
8 $this->travel(5)->minutes();
9 $this->travel(5)->hours();
10 $this->travel(5)->days();
11 $this->travel(5)->weeks();
12 $this->travel(5)->years();
13 
14 // Freeze time and resume normal time after executing closure...
15 $this->freezeTime(function (Carbon $time) {
16 // ...
17 });
18 
19 // Travel into the past...
20 $this->travel(-5)->hours();
21 
22 // Travel to an explicit time...
23 $this->travelTo(now()->subHours(6));
24 
25 // Return back to the present time...
26 $this->travelBack();
27}

Comments

No Comments Yet

“Laravel” is a Trademark of Taylor Otwell.
The source documentation is released under MIT license. See laravel/docs on GitHub for details.
The translated documentations are released under MIT license. See cornch/laravel-docs-l10n on GitHub for details.