Mock Testing in Laravel

Aliu Wahab
Apr 5 · 5 min read

One of Laravel’s gift to developers is to offer you in their opinions what they think you will need as a developer (some say they are opinionated) but for me after years of using it, I can only say, the Team knows what developers need (because they have done it over and over again) and provide the tools for us to succeed. One of such beautiful things the framework offers is a comprehensive pre-configured testing layer (extending PHPUnit).

They didn’t stop there, they added a mocking layer for certain processes and objects, this helps developers in writing test cases to “mock” certain aspects of your application so they are not actually executed during a given test”.

E.g, when testing a controller activity that fires an email, or stores some file, or queues etc, you don’t need the application to actually send an email etc for you to write your test to pass, hence mocking.

In this short tutorial, we’re going to use the Laravel Bus Fake “Mocking” facade for testing to explain the concept of mocking in Laravel with TDD. A feature offered within Laravel that helps to carry a test on certain aspects of the application without necessarily triggering the action.

Assuming you have set up your PHPUnit configuration for your Laravel testing. If you haven’t, I suggest setting up a brand new Laravel app project and add this configuration to your PHPUnit (Note you can use any configuration of your choice).

<env name=”DB_CONNECTION” value=”sqlite”/><env name=”DB_DATABASE” value=”:memory:”/>

This sets the database type for testing to SQLite and the database to be “in memory”. I am going to use the Bus Fake class to explain the whole mocking in Laravel for writing test cases for certain functionality and that gives you a strong base to understand mocking emails, queues, notification etc using their respective fake classes. This process is similar, the difference is just the Fake classes and the assertions the offer.

We should do TDD, so let’s create our test first.

Testing by wishful thinking (learnt from Adam Wathan), you know something does not exist, but you write your test as if it is there, it will fail, you write the code to make it pass (TDD).

We create our test class i.e “ExampleTest” in the “Feature” directory within our “tests” folder.

The code of the ExampleTest is:

Explanation of our test class.

We create only one test function call: bus_fake_test(). First disable the exception handling (so we can get any exception thrown without Laravel formatting it, when the test passes, I encourage you to take it off). Then the most important line:

Bus::fake();

Calling the Bus facade’s fake method. This provides us access to various assertions on the Bus class (we will talk more about that in a moment).

Of course, we leverage Laravels factory class to create a user.

$user = factory(User::class)->create();

We’re using the UserFactory class that Laravel at the time of this article automatically adds (see in the factories folder within the database folder).

Then we make a GET request to a URL:

$this->get('/example/job');

Next is the assertions:

Bus::assertDispatched(ExampleJob::class, function ($job) use ($user) {
return $job->user->id === $user->id;
});

THIS IS WHY WE ARE HERE: The “Bus::fake();” provides us with relevant assertions that we can make on any Job class like the ExampleJob::class.

The “assertDispatched” assertion is to make sure the job was fired or dispatched.

In a moment, I will point you to all the various assertions you can make.

If you run this test,

vendor/bin/phpunit --filter bus_fake_test

Your PC will explode (kidding), it will fail. Let us make it pass.

First Failure:

Symfony\Component\HttpKernel\Exception\NotFoundHttpException: GET http://localhost/example/job

This squawking that there is no route example/job that we called in the test above. So head to your routes file, web.php and add a route closure: This route could have been linked to a controller but for simplicity, I will make it a closure.

Route::get('/example/job', function () {});

If you run the test again, you will get:

The expected [App\Jobs\ExampleJob] job was not dispatched.
Failed asserting that false is true.

But don’t blame Laravel, you just returned a closure and you made an assertion that a certain ExampleJob::class has been dispatched.

In fact, we don’t have a class like that. Let’s create the ExampleJob class with the line:

“php artisan make:job ExampleJob”

The content is:

The Job class expects a User object in its constructor(it can be any data you wish to have access to within the job), then access the user object name to log it. It is within the handle method that you can carry out any action within a job, like sending an email, generating an invoice etc. depending on the data you pass to the constructor. You can do a lot with jobs, they are pretty powerful.

Here, in the handle method, we’re just logging the user name.

If we run our test, nothing will change from the previous error because the ExampleJob class has nothing to do with the route that we’re getting the response from to make our assertions. So let us do that.

Route::get('/example/job', function () {
$user = \App\User::first();
\App\Jobs\ExampleJob::dispatch($user);
});

Here, we’re getting the user and passing the user object into the ExampleJob class dispatch method, this is because we demanded for a user object in the ExampleJob constructor method.

NOTE: The job class has various methods like the dispatch and dispatchNow.

Now let us run our test, with any luck as Adam Wathan will say, it should be green (pass).

NOW:

Some of the assertions (as of now) on the

*Bus::fake();

include:

assertDispatched($command, $callback = null)
assertDispatchedTimes($command, $times = 1)
assertNotDispatched($command, $callback = null)
dispatched($command, $callback = null)
hasDispatched($command)
dispatch($command)
dispatchNow($command, $handler = null) etc.

You can use all the above to carry out assertions on your Laravel job classes without necessary firing the job.

NOTE:

As mentioned earlier, we also have Mocking for Event, Mail, Notification Queue, Storage etc.

So, for you to mock any of the mentioned objects, you just need to call the respective fake facade within the test method and use the respective assertions.

Short Examples from the Laravel Docs:

To mock Event, you need to pull in the event fake facade like:

And you can call various assertions. To see the assertions for Event mocking just search for EventFake class with your editor and look within the class, you will see various assertions offered by the event class. In fact, for the various mocks just search for the respective mock (Event, Mail, Notification, Queue, and Storage){Mock}Fake and you will see the various assertions types available.

I hope you’ve learnt something about mocking in testing within Laravel. Keep Learning.

The Andela Way

A pool of thoughts from the brilliant people at Andela

Thanks to Rose Maina.

Aliu Wahab

Written by

I Write Web and Mobile Apps: => PHP (Laravel) JavaScript (Vue.js) and Android Apps(Java).

The Andela Way

A pool of thoughts from the brilliant people at Andela