Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m Advertisement # Testing Laravel Controllers Difficulty:AdvancedLength:LongLanguages: Testing controllers isn't the easiest thing in the world. Well, let me rephrase that: testing them is a cinch; what's difficult, at least at first, is determining what to test. Should a controller test verify text on the page? Should it touch the database? Should it ensure that variables exist in the view? If this is your first hay-ride, these things can be confusing! Let me help. Controller tests should verify responses, ensure that the correct database access methods are triggered, and assert that the appropriate instance variables are sent to the view. The process of testing a controller can be divided into three pieces. • Isolate: Mock all dependencies (perhaps excluding the View). • Call: Trigger the desired controller method. • Ensure: Perform assertions, verifying that the stage has been set properly. ## The Hello World of Controller Testing The best way to learn these things is through examples. Here's the "hello world" of controller testing in Laravel. Laravel leverages a handful of Symfony's components to ease the process of testing routes and views, including HttpKernel, DomCrawler, and BrowserKit. This is why it's paramount that your PHPUnit tests inherit from, not PHPUnit\_Framework\_TestCase, but TestCase. Don't worry, Laravel still extends the former, but it helps setup the Laravel app for testing, as well as provides a variety of helper assertion methods that you are encouraged to use. More on that shortly. In the code snippet above, we make a GET request to /posts, or localhost:8000/posts. Assuming that this line is added to a fresh installation of Laravel, Symfony will throw a NotFoundHttpException. If working along, try it out by running phpunit from the command line. In human-speak, this essentially translates to, "Hey, I tried to call that route, but you don't have anything registered, fool!" As you can imagine, this type of request is common enough to the point that it makes sense to provide a helper method, such as $this->call(). In fact, Laravel does that very thing! This means that the previous example can be refactored, like so:

Though we'll stick with the base functionality in this chapter, in my personal projects, I take things a step further by allowing for such methods as $this->get(), $this->post(), etc. Thanks to PHP overloading, this only requires the addition of a single method, which you could add to app/tests/TestCase.php.

Now, you're free to write $this->get('posts') and achieve the exact same result as the previous two examples. As noted above, however, let's stick with the framework's base functionality for simplicity's sake. To make the test pass, we only need to prepare the proper route. Running phpunit again will return us to green. ## Laravel's Helper Assertions A test that you'll find yourself writing repeatedly is one that ensures that a controller passes a particular variable to a view. For example, the index method of PostsController should pass a $posts variable to its associated view, right? That way, the view can filter through all posts, and display them on the page. This is an important test to write!

If it's that common a task, then, once again, wouldn't it make sense for Laravel to provide a helper assertion to accomplish this very thing? Of course it would. And, of course, Laravel does!

Illuminate\Foundation\Testing\TestCase includes a number of methods that will drastically reduce the amount of code needed to perform basic assertions. This list includes:

• assertViewHas
• assertResponseOk
• assertRedirectedTo
• assertRedirectedToRoute
• assertRedirectedToAction
• assertSessionHas
• assertSessionHasErrors

The following examples calls GET /posts and verifies that its views receives the variable, $posts. Tip: When it comes to formatting, I prefer to provide a line break between a test's assertion and the code that prepares the stage. assertViewHas is simply a bit of sugar that inspects the response object - which is returned from $this->call() - and verifies that the data associated with the view contains a posts variable.

When inspecting the response object, you have two core choices.

• $response->getOriginalContent(): Fetch the original content, or the returned View. Optionally, you may access the original property directly, rather than calling the getOriginalContent method. • $response->getContent(): Fetch the rendered output. If a View instance is returned from the route, then getContent() will be equal to the HTML output. This can be helpful for DOM verifications, such as "the view must contain this string."

Let's assume that the posts route consists of:

Should we run phpunit, it will squawk with a helpful next step message:

To make it green, we simply fetch the posts and pass it to the view.

One thing to keep in mind is that, as the code currently stands, it only ensures that the variable, $posts, is passed to the view. It doesn't inspect its value. The assertViewHas optionally accepts a second argument to verify the value of the variable, as well as its existence. With this modified code, unles the view has a variable, $posts, that is equal to foo, the test will fail. In this situation, though, it's likely that we'd rather not specify a value, but instead declare that the value be an instance of Laravel's Illuminate\Database\Eloquent\Collection class. How might we accomplish that? PHPUnit provides a helpful assertInstanceOf assertion to fill this very need!

## Paths

One thing that we haven't considered in this test is validation. There should be two separate paths through the store method, dependent upon whether the validation passes:

1. Redirect back to the "Create Post" form, and display the form validation errors.
2. Redirect to the collection, or the named route, posts.index.

As a best practice, each test should represent but one path through your code.

This first path will be for failed validation.

The code snippet above explicitly declares which errors should exist. Alternatively, you may omit the argument to assertSessionHasErrors, in which case it will merely verify that a message bag has been flashed (in translation, your Redirection includes withErrors(\$errors)).

Now for the test that handles successful validation.

The production code for these two tests might look like:

Notice how the Validator is nested directly in the controller? Generally, I'd recommend that you abstract this away to a service. That way, you can test your validation in isolation from any controllers or routes. Nonetheless, let's leave things as they are for simplicity's sake. One thing to keep in mind is that we aren't mocking the Validator, though you certainly could do so. Because this class is a facade, it can easily be swapped out with a mocked version, via the Facade's shouldReceive method, without us needing to worry about injecting an instance through the constructor. Win!

From time to time, you'll find that a method that needs to be mocked should return an object, itself. Luckily, with Mockery, this is a piece of cake: we only need to create an anonymous mock, and pass an array, which signals the method name and response value, respectively. As such:

will prepare an object, containing a fails() method that returns true.

## Repositories

To allow for optimal flexibility, rather than creating a direct link between your controller and an ORM, like Eloquent, it's better to code to an interface. The considerable advantage to this approach is that, should you perhaps need to swap out Eloquent for, say, Mongo or Redis, doing so literally requires the modification of a single line. Even better, the controller doesn't ever need to be touched.

Repositories represent the data access layer of your application.

What might an interface for managing the database layer of a Post look like? This should get you started.

This can certainly be extended, but we've added the bare minimum methods for the demo: all, find, and create. Notice that the repository interfaces are being stored within app/repositories. Because this folder is not autoloaded by default, we need to update the composer.json file for the application to reference it.

When a new class is added to this directory, don't forget to composer dump-autoload -o. The -o, (optimize) flag is optional, but should always be used, as a best practice.

If you attempt to inject this interface into your controller, Laravel will snap at you. Go ahead; try it out and see. Here's the modified PostController, which has been updated to inject an interface, rather than the Post Eloquent model.

If you run the server and view the output, you'll be met with the dreaded (but beautiful) Whoops error page, declaring that "PostRepositoryInterface is not instantiable."

If you think about it, of course the framework is squawking! Laravel is smart, but it's not a mind reader. It needs to be told which implementation of the interface should be used within the controller.

For now, let's add this binding to app/routes.php. Later, we'll instead make use of service providers to store this sort of logic.

Verbalize this function call as, "Laravel, baby, when you need an instance of PostRepositoryInterface, I want you to use EloquentPostRepository."

app/repositories/EloquentPostRepository will simply be a wrapper around Eloquent that implements PostRepositoryInterface. This way, we're not restricting the API (and every other implementation) to Eloquent's interpretation; we can name the methods however we wish.

Some might argue that the Post model should be injected into this implementation for testability purposes. If you agree, simply inject it through the constructor, per usual.

That's all it should take! Refresh the browser, and things should be back to normal. Only, now, your application is far better structured, and the controller is no longer linked to Eloquent.

Let's imagine that, a few months from now, your boss informs you that you need to swap Eloquent out with Redis. Well, because you've structured your application in this future-proof way, you only need to create the new app/repositories/RedisPostRepository implementation:

And update the binding:

Instantly, you're now leveraging Redis in your controller. Notice how app/controllers/PostsController.php was never touched? That's the beauty of it!

## Structure

So far in this lesson, our organization has been a bit lacking. IoC bindings in the routes.php file? All repositories grouped together in one directory? Sure, that may work in the beginning, but, very quickly, it'll become apparent that this doesn't scale.

In the final section of this article, we'll PSR-ify our code, and leverage service providers to register any applicable bindings.

PSR-0 defines the mandatory requirements that must be adhered to for autoloader interoperability.

A PSR-0 loader may be registered with Composer, via the psr-0 object.

The syntax can be confusing at first. It certainly was for me. An easy way to decipher "Way": "app/lib/" is to think to yourself, "The base folder for the Way namespace is located in app/lib." Of course, replace my last name with the name of your project. The directory structure to match this would be:

• app/
• lib/
• Way/

Next, rather than grouping all repositories into a repositories directory, a more elegant approach might be to categorize them into multiple directories, like so:

• app/
• lib/
• Way/
• Storage/
• Post/
• PostRepositoryInterface.php
• EloquentPostRepository.php

It's vital that we adhere to this naming and folder convention, if we want the autoloading to work as expected. The only remaining thing to do is update the namespaces for PostRepositoryInterface and EloquentPostRepository.

And for the implementation:

There we go; that's much cleaner. But what about those pesky bindings? The routes file may be a convenient place to experiment, but it makes little sense to store them there permanently. Instead, we'll use service providers.

Service providers are nothing more than bootstrap classes that can be used to do anything you wish: register a binding, hook into an event, import a routes file, etc.

A service provider's register() will be triggered automatically by Laravel.

To make this file known to Laravel, you only need to include it in app/config/app.php, within the providers array.

Good; now we have a dedicated file for registering new bindings.

### Updating the Tests

With our new structure in place, rather than mocking the Eloquent model, itself, we can instead mock PostRepositoryInterface. Here's an example of one such test:

However, we can improve this. It stands to reason that every method within PostsControllerTest will require a mocked version of the repository. As such, it's better to extract some of this prep work into its own method, like so:

Now, if you want to be super-fly, and are willing to add a touch of test logic to your production code, you could even perform your mocking within the Eloquent model! This would allow for:

Behind the scenes, this would mock PostRepositoryInterface, and update the IoC binding. You can't get much more readable than that!

Allowing for this syntax only requires you to update the Post model, or, better, a BaseModel that all of the Eloquent models extend. Here's an example of the former:

If you can manage the inner "Should I be embedding test logic into production code" battle, you'll find that this allows for significantly more readable tests.

It feels good, doesn't it? Hopefully, this article hasn't been too overwhelming. The key is to learn how to organize your repositories in such a way to make them as easy as possible to mock and inject into your controllers. As a result of that effort, your tests will be lightning fast!