cult3

Getting started with testing Laravel 4 Models

May 20, 2013

Table of contents:

  1. Installing PHPUnit
  2. Writing your first test
  3. Setting up the test environment database
  4. Testing the User Model
  5. Conclusion

Last week I looked at what are Models in MVC applications, what are the characteristics of a Model and how to make your first Model in Laravel 4.

An integral part of writing high quality web applications is having automated tests that can ensure that your entire application is functioning correctly. In a Continuous Integration (CI) set up, having automated tests is extremely important to ensure that new changes don’t break the current code.

This isn’t a tutorial on Test Driven Development, so I’m going to assume that you already know what it is, how it is structured and what we are trying to achieve. If this is all new to you, take a look at my tutorial What is Test Driven Development?.

If you haven’t already read last week’s tutorial, you will probably want to read that first as it gives a lot of background to what I’m going to be doing in this tutorial, Setting up your first Laravel 4 Model.

Installing PHPUnit

In order to write automated tests, we need to install a fantastic package called PHPUnit.

If you are new to PHPUnit, you might want to read my introductory tutorial Getting started with PHPUnit first.

To install PHPUnit, add the following line to your composer.json file.

{
    "require-dev": {
        "phpunit/phpunit": "3.7.*"
    }
}

Next run the composer update command to install the package:

$ composer update

Now that you have PHPUnit installed, you can run automated tests from the command line. Laravel comes with one example test already set up. To make sure you have PHPUnit correctly set up, run the following command from Terminal:

$ vendor/bin/phpunit

You should get the following line returned if everything went ok:

OK (1 test, 2 assertions)

To make things easier for myself, I like to set an alias to PHPUnit so I don’t have to type of the vendor/bin bit every time I want to run the command.

alias phpunit='vendor/bin/phpunit'

Writing your first test

When writing unit tests, you want to concentrate on only one thing in particular. So for each Model that you have in your system, you should have exactly one test file that contains all of the automated tests. If you find yourself writing tests for things in more than one place, you will probably be doing something wrong.

All of the tests in a Laravel application are kept under app/tests.

If you look inside that directory, you should find the ExampleTest.php. When we ran the phpunit command from the command line earlier, this is the file of tests that was run. As you can see, there is one test with 2 assertions. You can just delete this file.

Laravel doesn’t force you to structure your tests directory in any particular way. You can keep all of your tests under the tests directory, or you can put them in more specific sub folders. I’m going to be putting my tests in sub folders for Models and Controllers, but I might change this in the future. For example, a common structure is to separate your test files into unit, functional and integration directories, but at the end of the day, it doesn’t really matter.

UPDATE! I have decided to move my folders into unit, functional and integration.

So create a new directory called models app/tests/models and create a new file under that directory called UserTest.php.

Copy the following code to create your User Test file:

class UserTest extends TestCase
{
}

As you can see, a test is simply a class that extends from the TestCase class. If you look under the app/tests directory, you will see the TestCase.php file that we are extending from. This is simply how Laravel sets up the testing environment and extends from PHPUnit so that we can write tests with all of the right methods. You don’t need to worry about this, just make sure that all of your test classes extend from TestCase and everything will automatically work correctly.

Each individual test should be written as a public method. The name of the method should be a descriptive name of what you are testing for. For example:

public function testThatTrueIsTrue()
{
    $this->assertTrue(true);
}

If you run phpunit once again you will see that this test passed successfully with one assertion.

OK (1 test, 1 assertion)

Setting up the test environment database

As I mentioned in my Migration tutorial, Laravel makes it incredibly easy to switch out databases. This means for example, we can use a different database when in the testing environment, but we don’t actually have to change any of our code.

Under the app/config/testing directory, create a new file called database.php and copy the following configuration details:

return array(
    'default' => 'sqlite',
    'connections' => array(
        'sqlite' => array(
            'driver' => 'sqlite',
            'database' => ':memory:',
            'prefix' => ""
        ),
    )
);

Now whenever your application is in the testing environment, Laravel will automatically use the SQLite in-memory database instead of hitting your actual database.

Why is this such a good thing? Testing with an in-memory database is much quicker than hitting an actual database. It also prevents the problem of having left over test data in your database every time you run your tests.

Next we need to hijack the TestCase class to finish off the set up for using an in-memory database. This is because we need to migrate the database at the start of each test because the database will be empty to begin with.

Add the following two methods to your TestCase class:

class TestCase extends Illuminate\Foundation\Testing\TestCase
{
    /**
     * Default preparation for each test
     */
    public function setUp()
    {
        parent::setUp();

        $this->prepareForTests();
    }

    /**
     * Creates the application.
     *
     * @return Symfony\Component\HttpKernel\HttpKernelInterface
     */
    public function createApplication()
    {
        $unitTesting = true;

        $testEnvironment = "testing";

        return require __DIR__ . "/../../start.php";
    }

    /**
     * Migrate the database
     */
    private function prepareForTests()
    {
        Artisan::call("migrate");
    }
}

I discovered this technique after reading this fantastic post Testing Like a Boss in Laravel: Models by Zizaco Zizuini. We will be using more of Zizaco’s packages over the course of creating Cribbb, so go and checkout his work and give him mad props for being an awesome contributor to the Laravel community.

Testing the User Model

Now that we have everything set up, we can start writing some tests. Currently our User model only really has validation that we can write tests for.

The first thing we will test for is that a username is a required field:

/**
 * Username is required
 */
public function testUsernameIsRequired()
{
    // Create a new User
    $user = new User;
    $user->email = "name@domain.com";
    $user->password = "password";
    $user->password_confirmation = "password";

    // User should not save
    $this->assertFalse($user->save());

    // Save the errors
    $errors = $user->errors()->all();

    // There should be 1 error
    $this->assertCount(1, $errors);

    // The username error should be set
    $this->assertEquals($errors[0], "The username field is required.");
}

So first we create a new user that does not have a username.

Next, we attempt to save the user and we assert that the return value isfalse.

Next, we grab the errors and we make sure there is only one.

And finally we assert that the correct error has been set.

Now if you run PHPUnit again, you should get the following response:

$ phpunit
$ OK (1 test, 3 assertions)

And that is how you would write a validation test for the username property of the User model. Now strictly speaking, you shouldn’t have to write tests for your validation because we are using the Ardent package that will already have these tests. But, I think writing validation tests is a nice and easy way to get into thinking in the mindset of Test Driven Development.

Conclusion

That was an introductory tutorial on setting up your automated tests in a Laravel project. Hopefully you now have a good understanding on why you need automated tests as well as how to structure them and how to run them.

A good analogy for automated testing is that it is like having breaks on your car. If a car didn’t have breaks, the driver would have to drive very slowly because it would be dangerous. Automated testing allows you as a developer to work much quicker because it provides you with a safety net if something goes wrong.

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.

In next Monday’s tutorial we will be looking at writing more advanced automated tests using fixtures!

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.