Search code examples
phplaravelphpunitlaravel-5

How to Migrate and seed before the full test suite in Laravel with in memory database?


I'm trying to set up the test environment in my Laravel project. I'm using http://packalyst.com/packages/package/mayconbordin/l5-fixtures with json for the seeding with a sqlite in memory database and calling:

Artisan::call('migrate');
Artisan::call('db:seed');

in my setUp function but this is executed before every single test which it can grow to thousands in this project.

I tried the setUpBeforeClass but it didn't work. I think there because the createApplication method is called in every test and that reset the whole application and also wasn't loading the fixtures from the json probably for the same reason.


Solution

  • This is how I did it in case someone else is struggling with the same, I created a base testClase class that inherits from Laravel's and did this:

    /**
     * Creates the application.
     *
     * @return \Illuminate\Foundation\Application
     */
    public function createApplication()
    {
        return self::initialize();
    }
    
    private static $configurationApp = null;
    public static function initialize(){
    
        if(is_null(self::$configurationApp)){
            $app = require __DIR__.'/../bootstrap/app.php';
    
            $app->loadEnvironmentFrom('.env.testing');
    
            $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
    
            if (config('database.default') == 'sqlite') {
                $db = app()->make('db');
                $db->connection()->getPdo()->exec("pragma foreign_keys=1");
            }
    
            Artisan::call('migrate');
            Artisan::call('db:seed');
    
            self::$configurationApp = $app;
            return $app;
        }
    
        return self::$configurationApp;
    }
    
    public function tearDown()
    {
        if ($this->app) {
            foreach ($this->beforeApplicationDestroyedCallbacks as $callback) {
                call_user_func($callback);
            }
    
        }
    
        $this->setUpHasRun = false;
    
        if (property_exists($this, 'serverVariables')) {
            $this->serverVariables = [];
        }
    
        if (class_exists('Mockery')) {
            Mockery::close();
        }
    
        $this->afterApplicationCreatedCallbacks = [];
        $this->beforeApplicationDestroyedCallbacks = [];
    }
    

    I overwrote the createApplication() and tearDown() methods. I changed the first one to use the same $app configuration and remove the part of the teardown() where it flush $this->app.

    Every other of my test has to inherit from this TestClass and that's it.

    Everything else didn't work. This works even with in memory database, it's 100s times faster.

    if you are dealing with user session, once you log the user in you will have to log him out in tear down, otherwise the user will be logged in because the app environment is never reconstructed or you can do something like this to refresh the application every time you want:

    protected static $applicationRefreshed = false;
    
    /**
     * Refresh the application instance.
     *
     * @return void
     */
    protected function forceRefreshApplication() {
        if (!is_null($this->app)) {
            $this->app->flush();
        }
        $this->app = null;
        self::$configurationApp = null;
        self::$applicationRefreshed = true;
        parent::refreshApplication();
    }
    

    And add this to the tearDown() before the $this->setUphasRun = false;:

    if (self::$applicationRefreshed) {
            self::$applicationRefreshed = false;
            $this->app->flush();
            $this->app = null;
            self::$configurationApp = null;
    }