Jul 15, 2013
Table of contents:
In last week’s post I looked at using Repositories in Laravel 4 to abstract the database layer away from the Controller. This enables you to easily switch out the type of database or the specific ORM that you want to use without having to change any of your Controller code in your actual application.
This week I want to look at how you should structure your Controllers to make them testable. Testing your Controllers is a critical aspect of building a solid web application, but it is important that you only tests the appropriate bits of your application.
Fortunately, Laravel 4 makes separating the concerns of your Controller really easy. This makes testing your Controllers really straight forward as long as you have structured them correctly.
So let’s get started!
Before I get into how to structure your Controllers for testability, first its important to understand what exactly we need to test for.
As I mentioned in Setting up your first Laravel 4 Controller, Controllers should only be concerned with moving data between the Model and the View. You don’t need to verify that the database is pulling the correct data, only that the Controller is calling the right method. Therefore your Controller tests should never touch the database.
This is really what I’m going to be showing you today because by default it is pretty easy to slip into coupling the Controller and the Model together.
As a way of illustrating what I’m trying to avoid, here is an example of a Controller method:
public function index()
{
return User::all();
}
This is a bad practice because we have no way of mocking User::all();
and so the associated test will be forced to hit the database.
In order to get around this problem, we have to inject the dependency into the Controller. Dependency Injection is where you pass the class an instance of an object, rather than letting that object create the instance for its self.
By injecting the dependency into the Controller, we can pass the class a mock instead of the database instead of the actual database object itself during our tests. This means we can test the functionality of the Controller without ever touching the database.
As a general guide, anywhere you see a class that is creating an instance of another object it is usually a sign that this could be handled better with dependency injection. You never want your objects to be tightly coupled and so by not allowing a class to instantiate another class you can prevent this from happening.
Laravel 4 has a beautiful way of handling Dependency Injection. This means you can resolve classes without any configuration at all in many scenarios.
This means that if you pass a class an instance of another class through the constructor, Laravel will automatically inject that dependency for you!
Basically, everything will work without any configuration on your part.
So now you understand the problem and the theory of the solution, we can now fix the Controller so it isn’t coupled to the database.
If you remember back to last week’s post on Laravel Repositories, you might have noticed that I already fixed this problem.
So instead of doing:
public function index()
{
return User::all();
}
I did:
public function __construct(User $user)
{
$this->user = $user;
}
/**
* Display a listing of the resource.
*
* @return Response
*/
public function index()
{
return $this->user->all();
}
When the UserController
class is created, the __construct
method is automatically run. The __construct
method is injected with an instance of the User
repository, which is then set on the $this->user
property of the class.
Now whenever you want to use the database in your methods, you can use the $this->user
instance.
The real magic happens when you come to write your Controller tests. Now that you are passing an instance of the database to the Controller, you can mock the database instead of actually hitting the database. This will not only improve performance, but you won’t have any test data lying around after your tests.
First thing I’m going to do is to create a new folder under the tests
directory called functional
. I like to think of Controller tests as being functional tests because we are testing the incoming traffic and the rendered view.
Next I’m going to create a file called UserControllerTest.php
and write the following boilerplate code:
class UserControllerTest extends TestCase
{
}
If you remember back to my post, What is Test Driven Development?, I talked about Mocks as being, a replacement for dependent objects.
In order to create Mocks for the tests in Cribbb, I’m going to use a fantastic package called Mockery.
Mockery allows you to mock objects in your project so you don’t have to use the real dependency. By mocking an object, you can tell Mockery which method you would like to call and what you would like to be returned.
This enables you to isolate your dependencies so you only make the required Controller calls in order for the test to pass.
For example, if you wanted to call the all()
method on your database object, instead of actually hitting the database you can mock the call by telling Mockery you want to call the all()
method and it should return an expected value. You aren’t testing whether the database can return records or not, you only care about being able to trigger the method and deal with the return value.
Installing Mockery Like all good PHP packages, Mockery can be installed through Composer.
To install Mockery through Composer, add the following line to your composer.json
file:
{
"require-dev": {
"mockery/mockery": "dev-master"
}
}
Next, install the package:
composer install -dev
Now to set up Mockery, we have to create a couple of set up methods in the test file:
public function setUp()
{
parent::setUp();
$this->mock = $this->mock('Cribbb\Storage\User\UserRepository');
}
public function mock($class)
{
$mock = Mockery::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
The setUp()
method is run before any of the tests. Here we are grabbing a copy of the UserRepository
and creating a new mock.
In the mock()
method, $this->app->instance
tells Laravel’s IoC container to bind the $mock
instance to the UserRepository
class. This means that whenever Laravel wants to use this class, it will use the mock instead.
Next you can write your first Controller test:
public function testIndex()
{
$this->mock->shouldReceive('all')->once();
$this->call('GET', 'user');
$this->assertResponseOk();
}
In this test I’m asking the mock to call the all()
method once on the UserRepository
. I then call the page using a GET request and then I assert that the response was ok.
Testing Controllers shouldn’t be as difficult or as complicated as it is made out to be. As long as you isolate the dependencies and only test the right bits, testing Controllers should be really straight forward.
In next week’s tutorial, I’m going to take a much deeper look at using Mockery in your tests. Mockery is an essential package for writing good applications and it is so powerful it deserves a post dedicated to it.
This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.