Laravel unit testing in workbench note

Just want to note down my experiences when doing unit testing with Laravel.

Mockery

Use Mockery to mock all the dependencies if possible to save time when executing the tests.

Database Testing with Sqlite (in memory)

If the tests involve a very complex SQL query/database, then should first try to do the testing using SQLite in memory. This will speed up execution time a lot as the database will be destroyed and re-created every time a single method runs. A database running in memory will speed up the process.

However, there are some weird bug with Eloquent when handling the parameter bindings with Sqlite e.g. Integer fields are inserted as strings. Retrieving Eloquent models attributes are sometimes string instead of int also. Sometimes, I need to override the getter in model to force an int to be returned. For the query builder, sometimes I have no choice but inline the int parameter value within the query to fix the problem i.e. 'int' bindings get converted to string in some exception cases.

Database Testing in workbench

Because of the way workbench is setup, by default, the test cases can only extend from PHPUnit_Framework_TestCase. Extending from \Illuminate\Foundation\Testing\TestCase will not work (if run phpunit from package folder) as it requires loading of Laravel application. There are 2 solutions to this:

1) Use Orchestra testbench package which actually re-initialize and load the Laravel app environment.

2) Run the test cases from the app folder instead of the package folder.

I choose the second option as testbench heavily depends on the particular Laravel version and seems a bit heavy to set up. What I did is configure the phpunit.xml under the main app to include the package tests folder as a test suite:

<testsuite name="Permit">
    <directory>./workbench/nth/permit/tests/</directory>
</testsuite>

And then run:

$ phpunit --testsuite=Permit

Using this method, we can still extends from \Illuminate\Foundation\Testing\TestCase. Example setup:

https://github.com/thehung111/Permit/tree/master/tests/DB

Set up and Destroy Test Databases using Seeder

There is a slight difference using in-memory database vs mysql. In particular , there is no need to reset the DB if using in-memory sqlite db.

  1.  public function setUp()
  2.  {
  3.  parent::setUp();
  4.   
  5.  $this->resetEvents(); // this is important as Eloquent does not call boot method when restart test cases
  6.  $this->prepare_data_for_tests();
  7.  }
  8.   
  9.  // reference: https://github.com/laravel/framework/issues/1181
  10.  private function resetEvents()
  11.  {
  12.      // Define the models that have event listeners.
  13.      $models = array('\Nth\Permit\Models\Role', '\Nth\Permit\Models\ResourceAction');
  14.   
  15.      // Reset their event listeners.
  16.      foreach ($models as $model) {
  17.   
  18.          // Flush any existing listeners.
  19.          call_user_func(array($model, 'flushEventListeners'));
  20.   
  21.          // Reregister them.
  22.          call_user_func(array($model, 'boot'));
  23.      }
  24.  }
  25.   
  26.          public function prepare_data_for_tests()
  27.  {
  28.  // Note: reset can only be called if test database is mysql
  29.  // currently use sqlite for testing this is unnecessary
  30.  if(Config::get('database.default') != 'sqlite')
  31.  Artisan::call('migrate:reset');
  32.   
  33.  Artisan::call('migrate', array("--bench" => "nth/permit"));
  34.   
  35.  Artisan::call('db:seed', array("--class" => "\\Nth\\Permit\\Seeds\\PermitTestDatabaseSeeder"));
  36.   
  37.  }
comments powered by Disqus