Home » Code » Getting started with testing Laravel 4 Models

Getting started with testing Laravel 4 Models

Posted by on May 20th, 2013

Getting started with testing Laravel 4 Models
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:

<?php
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:

<?php
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 is false.

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.

To view a full listing of the tutorials in this series, click here.

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

Philip Brown

Hey, I'm Philip Brown, a designer and developer from Durham, England. I create websites and web based applications from the ground up. In 2011 I founded a company called Yellow Flag. If you want to find out more about me, you can follow me on Twitter or Google Plus.

  • Pingback: Laravel 4 Fixture Replacement with FactoryMuff | Culttt()

  • Simone Basso

    Hi! Thx for this tutorial.
    When I try to run phpunit (end of tutorial) it find an error dislike you:
    SQLSTATE[HY000]: General error: 1 no such table ‘user’

    • Hi Simone, it’s saying the user table doesn’t exist in your database. Have you run your migration?

      • Simone Basso

        yes, :
        private function prepareForTests() {
        Artisan::call(‘migrate’);
        }

        • Hmm, is there a mismatch with what your table is called somwhere, “user” or “users”. Have you named the table “user” in your migration, but the table property in the model is set to “users”?

          • Simone Basso

            i’ve follow your tutorial step by step.
            protected $table = ‘users’;
            and $user = new User;

          • Hmm, I’m not really sure then without seeing your code. Send me a copy of your project and I’ll take a look

          • Simone Basso

            Ok! where?

          • Haha, the email in my tests is my real email, phil@ipbrown.com

          • Simone Basso

            ahahahaha
            I’ve send it now

          • Ok, so I got it working by updating the dependancies through Composer. I’m not sure exactly what was breaking it, but after a couple of attempts I got phpunit to run correctly.

            Try updating Composer. You should also be using Git to keep up to date with releases of Laravel.

            Hope this helps :)

          • Simone Basso

            I’m sure use the latest version of laravel. i’ve update the composer a few time, but the problem isn’t solve. uff

          • Simone Basso

            sorry for the double post :(

          • This is what I did
            – I create the users table
            – Delete the test route in the routes file
            – Then I had to delete the users table
            – The migrations ran correctly
            – Then I removed phpunit from composer
            – Composer update
            – Re-add phpunit
            – Composer update

            And then it worked

          • Simone Basso

            not work

          • I’ve sent you back the files that I got working. You will have to uninstall phpunit and reinstall it for Windows

          • Simone Basso

            Thx very much!

  • Guest

    this is the terminal log:

    ←[31;1mE←[0m

    Time: 0 seconds, Memory: 6.50Mb

    There was 1 error:

    1) UserTest::testUsernameIsRequired

    Exception: SQLSTATE[HY000]: General error: 1 no such table: users (SQL: select *

    from “users” where “id” = ? limit 1) (Bindings: array (

    0 => 1,

    ))

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelvendorla

    ravelframeworksrcIlluminateDatabaseConnection.php:502

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelvendorla

    ravelframeworksrcIlluminateDatabaseConnection.php:475

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelvendorla

    ravelframeworksrcIlluminateDatabaseConnection.php:269

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelvendorla

    ravelframeworksrcIlluminateDatabaseQueryBuilder.php:966

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelvendorla

    ravelframeworksrcIlluminateDatabaseQueryBuilder.php:956

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelvendorla

    ravelframeworksrcIlluminateDatabaseQueryBuilder.php:943

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelvendorla

    ravelframeworksrcIlluminateDatabaseEloquentBuilder.php:387

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelvendorla

    ravelframeworksrcIlluminateDatabaseEloquentBuilder.php:112

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelvendorla

    ravelframeworksrcIlluminateDatabaseEloquentBuilder.php:88

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelvendorla

    ravelframeworksrcIlluminateDatabaseEloquentBuilder.php:63

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelbootstrap

    compiled.php:5395

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelapproute

    s.php:31

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelvendorla

    ravelframeworksrcIlluminateFoundationstart.php:251

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelbootstrap

    start.php:61

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelapptests

    TestCase.php:21

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelvendorla

    ravelframeworksrcIlluminateFoundationTestingTestCase.php:39

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelvendorla

    ravelframeworksrcIlluminateFoundationTestingTestCase.php:29

    C:Program Files (x86)EasyPHP-DevServer-13.1VC9datalocalweblaravelapptests

    TestCase.php:9

    ←[37;41m←[2KFAILURES!

    ←[0m←[37;41m←[2KTests: 1, Assertions: 0, Errors: 1.

    ←[0m←[2K

  • I get this when I run phpunit

    {“error”:{“type”:”ErrorException”,”message”:”array_replace_recursive(): Argument #2 is not an array”,”file”:”/home/tariquesani/Projects/cribbb/bootstrap/compiled.php”,”line”:3787}}

    Any idea what is happening?

    • Hi, looks like a similar problem to Simone. Try following the steps that I’ve outline in the comments below

  • Simone Basso

    Your response,
    $ phpunit
    $ OK (1 test, 3 assertions)
    My response
    Tests: 1, Assertions: 3, Failures: 1

    It’s Ok?

    • Yeah, it’s because it’s expecting the error message to be in English. Chance the assertion and all you tests will pass

      • Simone Basso

        doh -.-‘… I had already solved then with your explanation … uff… I’m sorry I wasted your time further… now it work :)! See you at the next post!

  • hank

    Good tutorial, but I end up getting the error:

    “Non-static method Illuminate\Foundation\Artisan::call() should not be called statically, assuming $this from incompatible context”

    any ideas?

    • Hi Hank, through your code up on Github and I’ll take a look.

    • Borislav Borisov

      Hank did you fix the error? ;P

  • Pingback: How to structure testable Controllers in Laravel 4 | Culttt()

  • Pingback: Getting started with Mockery | Culttt()

  • Guest2112

    Great article!

  • ronaldvaneede

    Hi, If you test models, you are unit testing, right?
    Then why would you need a database, even if it is in memory?
    Unit tests should be executed in total isolation so you should mock the database.

    • Yeah that is true, but for certain things you can’t get away with mocking the database.

      I usually just let my data access unit tests hit a database in memory because it is easier than mocking everything. It is not perfect, and it will certainly be slower once you have thousands of tests, but I want to optimise for ease and understandability.

      I always mock the database for Controller tests, but I’m not too worried when it comes to Model tests.

      I think the whole “what should I test and what should I mock?” debate makes this far more complicated than it need be. There aren’t strict rules for how things should be done. You should just do whatever works for you and whatever allows you to build a great product.

  • Most probably I am missing something. I am new to Testing so I have some concerns, please advice.

    1. If you have such a method running on each test, means that you migrate/seed the DB even in tests that there is no such need? ( DB use)
    2. There is not a tearDown method to remove the migration on each test?

  • John Grimm

    Just in case anyone else was having trouble with TestCase.php:

    Using dynamic virtual hosts, I found myself unable to test the assertions because of an “Uncaught exception ‘PHPUNIT_Framework_Error_Warning'” .

    Changing the createApplication method so that it read:
    return require __DIR__.’/../../bootstrap/start.php’;

    …solved the issue.

    Maybe few will run into this, or perhaps it’s after an L4 update. A little hard to tell.

    Great tutorials!

    • Thanks John :)

      I’m not really sure why you would be getting that issue, but glad you found a solution!

      • John Grimm

        Yeah, I’m not 100% sure either. In Feb there was a commit that moved start.php to bootstrap/start.php:

        https://github.com/laravel/laravel/commits/master/app/tests/TestCase.php

        I’m not sure when you wrote this… perhaps prior to that commit? Either that, or I have an impressively complex and iffy Apache setup.

        I’d say that it’d be worth looking into if I wasn’t the only one mentioning it… Could also be your composer is version locked. I’ll stop with the guessing game now. :)

        • John Grimm

          Just noticed your git has it the way I do, so it’s just a typo in the source on this page.

          Thanks again!

          • Ah cool, thanks for looking into it John!

          • John Grimm

            My pleasure Philip!

  • Phil

    I did everything as instructed but I’m having issues. Please help.

    When I tried to run phpunit, this error shows up.

    UserTest::testUsernameIsRequired
    PDOException: could not find driver

    • Phil

      Never mind. It works now. I don’t know why it won’t work last saturday. sigh**

      • Sounds like you haven’t got the pdo_mysql module installed. What environment are you using? WAMP / MAMP / LAMP ?

        • Phil

          Thank you for your quick response.

          I’m currently using vagrant box (precise64). I checked my phpinfo and the pdo_mysql module is installed.

          • Hmm, I really don’t know then. I would perhaps double check your provisioning procedure to make sure you are pulling in everything that you need because that error would indicate that you don’t have the module installed.

          • Phil

            Ok. thank you. I tried it on my other machine with LAMP and it works. You are right. I need the pdo mysqli. thanks again and great article!

          • pakuize

            Pardon me but would that not actually be php_pdo_sqlite extension as we just set up sqlite for the testing envioment ?

          • Yeah that sounds right to me :)

      • Cowboy_X

        I resolved this issue by adding the following to composer.json and running “composer update”

        “require-dev”: {
        “doctrine/dbal”: “2.5.*@dev”
        },

  • Phil

    Just wondering if anybody get this error:

    PDOException: SQLSTATE[HY000]: General error: 1 Cannot add a NOT NULL column with default value NULL

    I fixed it by adding nullable() in my migration “$table->string(‘username’)->after(‘id’)->nullable()”. But on your recent article, you did not add the nullable() in your migration. Did I miss anything?

    Sorry for this dumb question, I’m just new to testing.

    • Hmm, sounds like you have something weird going on in your migrations. You are getting the error because you are trying to add a NOT NULL requirement to a column that has a default value of NULL. Throw your migrations up on GitHub and I’ll take a look.

      • Phil
        • Ah I see, yeah I would get rid of ->after('id')->nullable();.

          With this being a working project there’s probably going to be a few weird little instances like that. I would merge in changes from my original repo.

          Hope that helps!

          • Clay

            I’m having the same issue. Did you find a fix? I do not have ->after(‘id’)->nullable(); in my migration file…

          • Clay

            the test only passes if I do set username to nullable() in the migrations file, any idea why.. Either way great tutorials!!

          • Hmm, what is the error you are getting?

          • Clay

            Exception: SQLSTATE[HY000]: General error: 1 Cannot add a NOT NULL column with default value NULL (SQL: alter table “users” add column “username” varchar not null) (Bindings: array ())

          • JohnnyFittizio

            Hello! Great tutorial Philip!
            Just to let you know i had the same issue and resolve in the same way, adding nullable();

            Keep up the great work!

          • Thanks Johnny :)

          • Nick

            Thanks for your tutorial. This has been great for learning Laravel so I can begin developing a new project using the framework.

            I ran into the same problem: “General error: 1 Cannot add a NOT NULL column with a default value NULL”. I solved it by changing my testing database from a SQLITE database to a MYSQL database. The problem appears to be something about SQLITE. Running tests takes a little longer, but I didn’t like the idea of making key columns nullable.

          • Yeah seems like a lot of people (including me) found this error. I just moved the username column into my original migration and deleted the additional migration to fix the problem.

          • Luis Rolando Barzola

            Thanks Jonny, i fix with
            $table->string(‘username’)-> nullable();

          • As a side note to this problem. I ended up just rewriting my migration as one file, rather than one migration and then an update migrate. This fixed the problem for me and I didn’t have to use nullable because I don’t want the username to be nullable.

          • Clayt

            Just curious, no need to put much time into figuring this out if you don’t know right off top, I still am getting tremendous value from your tuts..

          • Hmm, I’m not sure. I would just add the username field to the original migration. It might be some weird thing going on because the column is being added after the fact. I’m not really sure to be honest. I’ll look into my migrations further tomorrow to see if I can see what’s going on

          • LEscher

            Ran into the same issue, I deleted the older migrations, dropped users table and redid the migration like philip said and it worked.

  • Kerel Lacy

    I’m getting a database does not exist when I run my phpunit test

    • Have you set up the test environment database correctly?

      • Patrick Cauley

        Do we need to create a sqlite database for the testing environment?

        • You don’t have to, but it’s going to be quicker than hitting an mysql database.

          • Patrick Cauley

            Sorry, I’m rather new to Laravel or PHP coming from a mostly WordPress and JavaScript background.

            I guess what I am asking is, do I create a sqlite database in order to pass these tests? In the same manner that I created a mysql database?

          • Ah I see what you mean. All you have to do is to create the config file that I mentioned above.

          • Patrick Cauley

            I’m building an app using Laravel and AngularJS, yours is the first good tutorial from scratch I’ve seen that incorporates both Laravel and a JS MVC. It reminds me a lot of the Ruby on Rails tutorial that’s popular by Michael Hartl

          • Thank you Patrick :) I read through Michael’s building a twitter client in RoR when I was first looked at rails and I really enjoyed it, so that means a lot to me :)

          • Patrick Cauley

            It’s giving me an error that there is no such table ‘users’

          • Have you got the prepareForTests method set up correctly?

  • Pingback: Optimise your Photoshop workflow | Culttt()

  • Pingback: How to build a Coming Soon page for your product | Culttt()

  • Hello,

    First thanks for a good article series of Laravel, much appriciated! I will go through it all.

    Although Im facing some difficulties. I have the same problem as Simone Baso. Im getting the following error when trying to run vendor/bin/phpunit

    1) ExampleTest::testUsernameIsRequired

    Exception: SQLSTATE[HY000]: General error: 1 no such table: users (SQL: select count(*) as aggregate from “users” where “user_email” = ?) (Bindings: array (

    0 => ‘xxxx@xxxxx.se’,

    ))

    I have tried the following – which you stated – without any success:

    – I create the users table- Delete the test route in the routes file
    – Then I had to delete the users table
    – The migrations ran correctly
    – Then I removed phpunit from composer
    – Composer update
    – Re-add phpunit
    – Composer update

    My code:

    app/tests/TestCase.php: http://pastebin.com/p9KS2HyG
    app/tests/models/UserTest.php: http://pastebin.com/rmZZGHE8
    app/routes.php: http://pastebin.com/ueEsfvdW

    What am I doing wrong and how do I fix it?

    • Push your project up to Github and I’ll take a look.

      • Is it okay if I email it you? Im pretty new to Github and having it as a private repository – so if Im not wrong Im not able to share it unless I make it public.

        • Yeah no problem. Or you could add me as a contributor? It’s probably going to be easier if I make a Pull Request rather than trying to explain what you need to change over email.

    • vineet chopdekar

      I’m having the same issue. Was this problem solved? If so, please do share the solution, thanks

  • longestdrive

    Hi. Great series of tutorials – testing is alien to me but gradually getting it – thank you. I am though having trouble testing a model/controller. The controller is basic as generated by the jeffrey way generators as is the model and the test. I changed it to use Ardent (and recently your package magniloquent).

    When I run a test that uses findorfail the test throws and error – Call to a member function fetchMock() on a non-object – if I switch the model and the test back to using Eloquent the test passes.

    I’ve posted a couiple of questions on this in the usual forums but no answers yet. I’d like to use Ardent or similar but how do I properly test? Does the method change of obtaining a mock object?

    • Thank you, glad you are finding them useful.

      Do you have your code up on Github?

      • longestdrive

        I have in a private repository. I’ve also pasted them here in this post: http://forums.laravel.io/viewtopic.php?id=15295.
        Been tweaking with the tests but still issues with ardent and fetchmock errors.
        Thank you

        • Hmm, I’m not really sure. To be honest I don’t bother mocking the database for unit tests I just create a test database in memory and hit that.

  • Etienne Wilhelm Marais

    Thanks again for a great article. I have a question about testing the UserRepository which is the abstraction of the UserModel for eloquent. I use it to inject the User into my controller.

    //class EloquentUserRepository implements UserRepository

    how would I test this? Am I correct in assuming that I could just inject it into the test __construct() method?

  • snugglezone

    Something wasn’t working for me using Ardent and Laravel 4.1. Had to use this branch of Ardent which fixes the problem but hasn’t been merged yet.

    Fix belongsTo() method to be compatible with Laravel 4.1.

    https://github.com/laravelbook/ardent/pull/133

    Instructions to use it with composer are at the bottom.

  • Etienne Wilhelm Marais

    In your prepareForTests() where you call the migrate, I added a db:seed command but the sqlite database does not seem to work when unit testing and adding seeds. Do you know why this is the case? Or am I forced to seed an actual test database?

    • Hmm, I would of thought it should work. Are you getting an error?

      • Etienne Wilhelm Marais

        Hi, Nevermind it was a problem with my seeder. The fill operaton changed, but I got it working again =D

  • Hey Philip, awesome tutorial.

    How can I test an application that has an existing database?

    • Thank you :)

      Hmm, I’m not sure, I’ve never added Laravel to an existing database. If you can map your database to Eloquent, you could probably do it from there.

    • Adam HighTower Sturrock

      Hi Marcel, this shouldn’t be too difficult. Once you have your Laravel workbench setup you can point the database.php config file to the existing database. Create models for the existing tables, either by naming them as singular versions of the table name (User for table ‘users’) or using the $table parameter in your Model. You can then use the Test Case as described above, have migrations and seeds ready to point to a testing database and use something like FactoryMuff to populate the test database with data.

  • mike

    Great article. I think it’s much simplier to test application in Laravel than in CakePHP. Laravel has a giant potiential to be number one!

    • Yeah for sure! Laravel is definitely the way forward :)

  • Just wanted to post something others might find useful. I’m working on
    porting an existing app to L4 but unfortunately I’m force to work
    around the existing DB schema. This makes testing a bit messy
    especially when my test migrations try to create a bunch of tables
    that already exist or vice versa when using ‘:memory:’.

    My solution was to make ‘test-only migrations’. First alter the
    following line in the prepareForTests() method in TestCase.php

    Artisan::call('migrate',
    array('--path' => 'app/database/migrations/testing'));

    Then require Laravel-Migration-Generator in your Composer dependencies:

    "barryvdh/laravel-migration-generator": "dev-master"
    (Read more at https://github.com/barryvdh/laravel-migration-generator)

    This will allow you to create migrations for existing tables in your
    database. In most cases you’ll be starting with a blank DB when you
    start your project but for those rare cases where you need to do
    things a bit different this should help a lot.

    • Wow, thank you Dalton! I’m sure that will come in handy for a lot of people! :)

      • David Pascaud-Blandin

        Thank you for the tip. This solved the CANNOT ADD NULL value to a NOT NULL collum issue. Read more on this issue at https://github.com/laravel/framework/issues/168

        I’ll add the detailed step for people still having troubles :

        Create a new directory in the app/database/migrations directory (example : app/database/migrations/testing)

        Follow the instructions in Dalton’s post above.

        Run composer update from your project’s directory

        Run php artisan migration-generate --path="./path/to/your/testing/directory" yourTableName

        For instance, with a directory named “testing” and a table named “posts” :

        Run php artisan migration-generate --path="./app/database/migrations/testing" posts

        This will create a new migration from an existing posts table from your sql database in the testing directory.

        Now you can run your tests !

    • beeblebrox3

      Thanks :D

  • Joseph Rex

    The latest phpunit is 4.1.2 I think you should change the composer package from “phpunit/phpunit”: “3.7.*” to “phpunit/phpunit”: “4.1.*”

  • Adam HighTower Sturrock

    Hi Philip, any chance of some more general advice on Database Testing in Laravel? I know database tests are infamous for being more difficult, complex and generally slower with PHP so do you have any notes on how Laravel improves on this? It certainly feels easier when you have Eloquent, FactoryMuff and Faker around. Is the performance hit less of an issue since you’re using an in-memory testing database as you said?

    • Yeah using an in-memory database will certainly be better than hitting a real database.

      I think generally speaking you should mock the database when the thing you are testing is not the concern of the database.

      For integration testing, use something like FactoryMuff to create fixtures and let your tests hit the in-memory database. Hitting the database for these kinds of tests isn’t an issue because otherwise your tests would be brittle and it would be difficult to be confident in your tests.

      Hope that helps :)

  • Klaas

    I am very, very happy with this tutorial series…!

    On this one though I ran into a problem I cannot solve. I used all the examples on a pristine laravel install which is working as expected until now. When I execute the last test I get the following error:

    There was 1 error:

    1) UserTest::testUsernameIsRequired
    IlluminateDatabaseQueryException: SQLSTATE[HY000]: General error: 1 no such table: users (SQL: select * from “users” where “id” = 1 limit 1)

    It looks like the test migration to the sqlite database hasn’t worked. Sqlite is installed, Previous migrations workes without any issue. The table ‘users’ exists in the mysql database.

    I followed (end checked) the procedure in this tutorial exactly.

    • Yeah I think it’s because I did something weird with the migrations. I would just manually change your migrations so it creates the tables correctly.

      • Klaas

        Hi Philip, thanks for your response. I do not know exactly how to do that… My mysql table ‘users’ exists allready in the mysql database so migration works (I think?).

        I assume it is complaining about the absense of ‘users’ in the sqlite memory version created by the test? How can I correct this? I really want to go on with this tutorial with a succesfull test.

        • Are you running the migrate command before your tests?

    • scruffmcbuff

      I am getting a very similar issue: General error: 1 table users has no column named username (SQL: insert
      into “users” (“username”, “email”, “password”, “password_confirmation”,
      “updated_at”, “created_at”) values (philipbrown, phil@ipbrown.com,
      deadgiveaway, deadgiveaway, 2014-09-01 13:23:40, 2014-09-01 13:23:40))

      I rerun ‘php artisan migrate’ and i get this error: SQLSTATE[HY000]: General error: 1 Cannot add a NOT NULL column with default
      value NULL (SQL: alter table “users” add column “username” varchar not nul
      l)

      Any help would be greatly appreciated with this. Not sure what I am doing wrong or if I need to make changes to migrations.

      • Yeah just get rid of the migrations and remake them again without the add column migration.

      • cgaviria

        Did you ever find a solution to this problem? I am having the EXACT same issue. The issue seems to be with the way laravel prepares the sql statements, and the values in the insert statement are missing quotes around them, thus causing the error.

        • Ricardo

          Laravel just doesn’t print the quotes, but they are there when executing the query/statement.

          The error in his comment is from having a users table without the username column, probably due to broken migration.

  • Nirmal Thapa

    I guess many of us had the exact problem of “cannot add null to not null…..”. Although I have sorted this out by moving additional username column to the original users table migration. It worked.
    But I still don’t know —
    Why it wasn’t working then? and Why it is working now?

    • I think it was just the strange way I had the migration set up. When you run in to problems like that it’s always good to start from a clean slate :)

  • Ben Dubuisson

    For some reason I get 1) UserTest::testFirstNameIsRequired

    IlluminateDatabaseQueryException: SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: users.first_name

    I suppose it returns the DB error before the validation, any idea?

    • Ben Dubuisson

      never mind you are testing the MODEL…

      • Yep, it’s because your database is expecting the first_name field to bet set on the Model :)

  • Roelof

    When I do the steps in Laravel 4 I get a message that start.php cannot be found.

  • Roelof

    When I make the first test I see a message that start.php cannot be found.