web-dev-qa-db-ja.com

Laravelインメモリデータベースで完全なテストスイートの前に移行してシードする方法は?

Laravelプロジェクトでテスト環境をセットアップしようとしています。使用しています http://packalyst.com/packages/package/mayconbordin/l5-fixtures メモリデータベースにsqliteをシードして呼び出すためのjsonを使用:

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

私のsetUp関数では、これは、このプロジェクトで数千に達する可能性のあるすべてのテストの前に実行されます。

setUpBeforeClassを試しましたが、機能しませんでした。 createApplicationメソッドがすべてのテストで呼び出され、アプリケーション全体がリセットされ、おそらくjsonからフィクスチャが読み込まれなかったためだと思います。同じ理由。

10
Franklin Rivero

これは、他の誰かが同じことに苦労している場合に備えて、私がそれを行った方法です。Laravelから継承する基本のtestClaseクラスを作成し、これを行いました。

_/**
 * 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 = [];
}
_

createApplication()メソッドとtearDown()メソッドを上書きしました。同じ_$app_構成を使用するように最初の構成を変更し、_$this->app_をフラッシュするteardown()の部分を削除しました。

私のテストの他のすべては、このTestClassから継承する必要があり、それだけです。

他のすべては機能しませんでした。これはインメモリデータベースでも機能し、100倍高速です。

ユーザーセッションを処理している場合は、ユーザーをログインしたら、破棄してログアウトする必要があります。そうしないと、アプリ環境が再構築されないため、ユーザーがログインするか、このようなものを追加してアプリケーションを更新できます。あなたが望むたびに:

_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();
}
_

そして、これを_$this->setUphasRun = false;_の前のtearDown()に追加します。

_if (self::$applicationRefreshed) {
        self::$applicationRefreshed = false;
        $this->app->flush();
        $this->app = null;
        self::$configurationApp = null;
}
_
11
Franklin Rivero

このコンテンツを使用してプロジェクトtestrunnerにファイルを作成します(テスト環境変数を使用して.env.testingファイルも準備します):

php artisan migrate:rollback --env=testing
php artisan migrate --env=testing --seed
vendor/bin/phpunit

そして、コマンドchmod +x testrunnerで実行する権限を与え、./testrunnerで実行します。それで全部です :)

1

上記のソリューションの主なアプローチは、すべてのテストに対してすべての移行を実行することです。テストごとに実行する移行とシードを指定するアプローチが好きです。

これにより、テストのタイミングが約70%短縮される可能性があるため、大規模なプロジェクトではより価値がある可能性があります(前述のようにsqliteインメモリDBを使用)。小さなプロジェクトの場合、それは多分少し面倒です。とにかく...

TestCaseでこれらを使用します。

/**
 * Runs migrations for individual tests
 *
 * @param array $migrations
 * @return void
 */
public function migrate(array $migrations = [])
{
    $path = database_path('migrations');
    $migrator = app()->make('migrator');
    $migrator->getRepository()->createRepository();
    $files = $migrator->getMigrationFiles($path);

    if (!empty($migrations)) {
        $files = collect($files)->filter(
            function ($value, $key) use ($migrations) {
                if (in_array($key, $migrations)) {
                    return [$key => $value];
                }
            }
        )->all();
    }

    $migrator->requireFiles($files);
    $migrator->runPending($files);
}

/**
 * Runs some or all seeds
 *
 * @param string $seed
 * @return void
 */
public function seed(string $seed = '')
{
    $command = "db:seed";

    if (empty($seed)) {
        Artisan::call($command);
    } else {
        Artisan::call($command, ['--class' => $seed]);
    }
}

次に、個々のテストで必要に応じて、migrate()とシードを呼び出します。例:

    $this->migrate(
        [
            '2013_10_11_081829_create_users_table',
        ]
    );
    $this->seed(UserTableSeeder::class);
1
markdwhite

オプション1

移行とシードを使用してデータベースをセットアップしてから、データベーストランザクションを使用するのはどうですか? ( https://laravel.com/docs/5.1/testing#resetting-the-database-after-each-test

次のような職人を介してテストデータベースを設定できるようにしたかったのです。

$ php artisan migrate --database=mysql_testing
$ php artisan db:seed --database=mysql_testing

ご想像のとおり、私はmysqlを使用していますが、これがsqliteで機能しない理由がわかりません。これが私のやり方です。

config/database.php

まず、現在のデータベース情報の下にあるconfig /database.phpファイルにテストデータベース情報を追加します。

'connections' => [
        'mysql' => [
            'driver'    => 'mysql',
            'Host'      => env('DB_Host', 'localhost'),
            'database'  => env('DB_DATABASE', 'forge'),
            'username'  => env('DB_USERNAME', 'forge'),
            'password'  => env('DB_PASSWORD', ''),
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
            'strict'    => false,
        ],
        'mysql_testing' => [
            'driver'    => 'mysql',
            'Host'      => env('DB_Host', 'localhost'),
            'database'  => env('DB_TEST_DATABASE'),
            'username'  => env('DB_USERNAME', 'forge'),
            'password'  => env('DB_PASSWORD', ''),
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
            'strict'    => false,
        ],
    ],

このようにする場合は、DB_TEST_DATABASEを。envファイルに追加することを忘れないでください。

DB_DATABASE=abc
DB_TEST_DATABASE=abc_test

phpunit.xml

.envファイルで指定された上書き値の下でphpunit.xmlファイルに設定された値。したがって、phpunitに「mysql」データベース接続の代わりに「mysql_testing」データベース接続を使用するように指示します。

<?xml version="1.0" encoding="UTF-8"?>
<phpunit>
    ...
    <php>
        ...
        <env name="DB_CONNECTION" value="mysql_testing"/>
</php>

テストクラス

私のテストクラスは次のようになります。

class MyTest extends \TestCase
{
    use \Illuminate\Foundation\Testing\DatabaseTransactions;

    public function testSomething()
    {

オプション2

ここでは、すべてのテストの前にデータベースがリセットされるため、オプション1を選択します。ただし、データベースを希望どおりに機能させることができる場合があります。

私は以前にこれを一度試しました、そしてそれはあなたのために働くかもしれません。

tests/TestCase.phpテストケースを拡張して、新しい.envファイル.env.testingをロードします

<?php

class TestCase extends Illuminate\Foundation\Testing\TestCase
{
    /**
     * The base URL to use while testing the application.
     *
     * @var string
     */
    protected $baseUrl = 'http://localhost';

    /**
     * Creates the application.
     *
     * @return \Illuminate\Foundation\Application
     */
    public function createApplication()
    {
        /** @var $app \Illuminate\Foundation\Application */
        $app = require __DIR__.'/../bootstrap/app.php';
        $app->loadEnvironmentFrom('.env.testing');

        $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();

        return $app;
    }
}

。env.testing

この新しい.envファイルを作成し、データベースの詳細を追加します

APP_ENV=testing
APP_DEBUG=true
APP_KEY=xxx

DB_CONNECTION=mysql
DB_Host=127.0.0.1
DB_DATABASE=abc_testing
DB_USERNAME=xxx
DB_PASSWORD=xxx

テストクラス

PDOを使用して、データベースを削除して再作成します。すべてを切り捨てるよりも簡単です。次に、職人を使用してデータベースを移行およびシードします。

class MyTest extends TestCase
{
    public static function setUpBeforeClass()
    {
        $config = parse_ini_file(".env.testing");
        $username = $config['DB_USERNAME'];
        $password = $config['DB_PASSWORD'];
        $database = $config['DB_DATABASE'];
        $Host = $config['DB_Host'];

        // Create test database
        $connection = new PDO("mysql:Host={$Host}", $username, $password);
        $connection->query("DROP DATABASE IF EXISTS " . $database);
        $connection->query("CREATE DATABASE " . $database);
    }

    public function testHomePage()
    {
        Artisan::call('migrate');
        Artisan::call('db:seed');

        $this->visit('/')
             ->see('Home')
             ->see('Please sign in')
             ->dontSee('Logout');
    }
0
Aine