We need to create an instance of Laravel, along with a database that PHPUnit can access to run the tests against real-world data sets. Not brittle mocks. Firstly you should be developing your packages in isolation for a number of reasons, one being workbench is now deprecated in Laravel 5.
So firstly we need to dev-require the Laravel framework into our project:
"require-dev": {
"phpunit/phpunit": "~4.0",
"phpspec/phpspec": "~2.1",
"laracasts/testdummy": "~2.0",
"laravel/laravel": "dev-develop"
},
Now we can create an abstract class called DbTestCase that all our tests will extend from. Within this class we will be spinning up an instance of Laravel and an in memory SQLite database for speed.
If we extend the native Laravel test class Illuminate\Foundation\Testing\TestCase some of the work has already been done for us. We simple need to create a method that returns an instance of Illuminate\Foundation\Application.
/**
* Boots the application.
*
* @return \Illuminate\Foundation\Application
*/
public function createApplication()
{
$app = require __DIR__.'/../vendor/laravel/laravel/bootstrap/app.php';
$app->register('Path\To\Your\PackageServiceProvider');
$app->make('Illuminate\Contracts\Console\Kernel')->bootstrap();
return $app;
}
notice the line $app->register('Path\To\Your\PackageServiceProvider'); this is important. Include your package service provider path here, so we register it with our Laravel instance that lives in our packages /vendor folder.
Now we have a Laravel application running, we need to setup the in-memory SQLite database. Simple, Laravel's TestCase has a setUp() function that is run before ever test, lets do it there:
/**
* Setup DB before each test.
*
* @return void
*/
public function setUp()
{
parent::setUp();
$this->app['config']->set('database.default','sqlite');
$this->app['config']->set('database.connections.sqlite.database', ':memory:');
$this->migrate();
}
I won't give to much explanation as its quite readable. As you can see on the last line we are also calling $this->migrate() which obviously runs our migrations each time we run a test giving us a fresh DB to test against. Lets look at how that works:
/**
* run package database migrations
*
* @return void
*/
public function migrate()
{
$fileSystem = new Filesystem;
$classFinder = new ClassFinder;
foreach($fileSystem->files(__DIR__ . "/../src/Migrations") as $file)
{
$fileSystem->requireOnce($file);
$migrationClass = $classFinder->findClass($file);
(new $migrationClass)->up();
}
}
Not to go into too much detail, basically what we are doing here is looking into the src/Migrations folder of the package requiring all the files and then running their migrations. Its rough and needs more safety checks (i will do that in the future) but it works.
Why not Artisan::call('migrate') ??
Simple! In Laravel 5 the command php artisan migrate --package='vendor/package' has been deprecated. Developers are now expected to create their own commands to generate and move migration files to the proper location in the application. This is a much more flexible approach.