pixelpeter's avatar

How to set up and define your database for integration testing

Despite the fact that "Laravel is build with testing in mind" I find this part not as intuitive as all the other great parts of the Laravel landscape. And the changes for the environment handling doens't make things easier.

I don't know if other devs see it the same way as I do.

So I'am willing to send a pull request to improve the documentation based on the response to this article.

For all the people who are struggling with phpunit and the integrated testing framework here's what to do.

Please keep in mind that most of this tips are related to seeding your database with seeders and not using model factories directly in your test methods to generate the minimal amount of data needed to get the test green.

1. Define you test database

config/database.php

This can be anything. MySQL, SQLite file based or like this example a SQLite db in memory

  'sqlite_testing' => [
     'driver'   => 'sqlite',
     'database' => ':memory:',
     'prefix'   => '',
 ],

2. Let phpunit know which database to use

phpunit.xml

This was for me the hardest part to figure out.

You need tp add <env name="DB_CONNECTION" value="sqlite_testing" /> to the definition of environment vars like this.

Now phpunit nows witch connectin to use, because in comparison to codeception phpunit is not reading the .env.testing file .

  <php>
      <env name="APP_ENV" value="testing"/>
      <env name="CACHE_DRIVER" value="array"/>
      <env name="SESSION_DRIVER" value="array"/>
      <env name="QUEUE_DRIVER" value="sync"/>

      <env name="DB_CONNECTION" value="sqlite_testing" />
  </php>

3. Automatically migrate your database before each test

tests/[YOUR-TEST-FILE]

You can use the provided DatabaseMigrations trait

  use Illuminate\Foundation\Testing\WithoutMiddleware;
  use Illuminate\Foundation\Testing\DatabaseMigrations;
  use Illuminate\Support\Facades\Config;

  class [YOUR-TEST-CLASS] extends TestCase
  {
    use DatabaseMigrations;

  public function [YOUR-TEST-FUNCTION]()
  {
    // do your tests
  }
  ...

4. How to seed your database before each test

Because phpunits setUp() is called before the DatabaseMigrations trait you can't use it to seed the database.

So here are two options:

Don't use the DatabaseMigrations trait

You can write your own setUp() and tearDown() methods

tests/[YOUR-TEST-FILE]

  ...
  public function setUp()
  {
      parent::setUp();

      $this->artisan('migrate');
      $this->artisan('db:seed');
  }

  public function tearDown()
  {
      $this->artisan('migrate:reset');
  }
  ...

Use your own DatabaseMigrations trait

Write your own DatabaseMigrations trait with an extra call to db:seed.

tests/support/DatabaseMigrations.php

  trait DatabaseMigrations
  {
      /**
       * @before
       */
      public function runDatabaseMigrations()
      {
          $this->artisan('migrate');
          $this->artisan('db:seed');

          $this->beforeApplicationDestroyed(function () {
              $this->artisan('migrate:rollback');
          });
      }
  }

Don't forget do add it to composer.json for autoloading

  ...
  },
  "autoload-dev": {
      "classmap": [
          "tests/TestCase.php",
          "tests/support/DatabaseMigrations.php"
      ]
  },
  ...

5. Extra: How to define the database for functional testing with CodeCeption

In codeception for you functional suite you would set your database configuration in functional.suite.yml like this:

config:
  Laravel5:
    environment_file: .env.testing

6. Extra: How to use define the database for acceptance testing with Behat

The Laracasts Laravel Behat extension allows you to create a .env.behat file to change the environment variables.

This article helped me a lot to figure all this out:

http://stackoverflow.com/a/27986561

Maybe this is helpful for somebody.

If I am partly or even completely wrong please let me know so I can improve this article (and myself)

0 likes
2 replies
CrtlAltDylan's avatar

Thank you! I can't believe how hard it is to figure out testing in Laravel.

For unit/integration tests I'm very happy with Codeception.

However there are a number of things that screw up using codeception:

  1. Without the Laravel5 module, you cannot use namespaced classes/facades in your application code
  2. If you use the Laravel5 module you cannot use it with the acceptance testing suite, even if you use that little snippet in the Laravel5 module docs.
  3. As far as I can tell with the Laravel5 module, you cannot authenticate as a user.
  4. In theory you should be able to use the PHP league faker library, but in fact you can't because it's facade based and codeception doesn't play well with that. Even the documentation on using the faker library admits this.
  5. Using Laravel's built in testing suite is relies on the phpunit.xml file for configs not an easy yml loading a .env.testing
  6. Laravel's testing documentation is not nearly as good as codeceptions. I wish I could just use codeception and be able to test all aspects.
christophrumpel's avatar

thx nice read! Feels like there could be a better solution with DataMigrationTrait and seeding.

Please or to participate in this conversation.