Running setup code before every test in a suite

2019-09-16 13:23发布

问题:

I've got a Laravel 5 application in development that has two test suits: Unit and Functional. I've defined these in phpunit.xml:

<testsuite name="Unit">
    <directory>./tests/unit</directory>
</testsuite>
<testsuite name="Functional">
    <directory>./tests/functional</directory>
</testsuite>

For the functional tests to run correctly, they need to have a valid database to work against. This means performing some setup before running the functional tests in order to migrate the database.

Normally I'd use the DatabaseMigrations trait in my tests to make sure the database is migrated before every test. Unfortunately, this slows our tests down significantly (we have hundreds). I switched the tests over to use the DatabaseTransactions trait, which makes them run a lot faster, but now the database isn't migrated before running the tests. If I run the testing suite on a fresh clone of the project, it fails because I haven't migrated the database before running the functional test suite.

The obvious solution to this was to add $this->artisan('migrate'); to the setUp method of tests/TestCase.php. But this has two problems:

  1. This causes the database to be migrated before every test, which is what I was trying to avoid in the first place.

  2. This also tries to migrate the database when running the unit testing suite, which is obviously not ideal.

What's the best way for me to ensure that the database for the functional tests is migrated before any tests are run, but only for the functional tests?

回答1:

You can create create a base testClase class that inherits from Laravel's TestCase and only your Functional suite classes can inherit the new one, and in your new class add 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 = [];
}

This works even with in memory database, it's 100 times faster.

PS : working for Laravel not Lumen Applications.