30
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

LaravelでRefreshDatabaseを使えない場合はどうするべきか

Posted at

RefreshDatabase はマイグレーションを使う場合にしか使えません。そういうときは DatabaseTransactions を使うといいよというお話です。

なお、古くからの Laravel 遣いには、目新しさが微塵も無い記事だと思うので、スルーしていただければと思います。

前提

  • Laravel 6.8.10

RefreshDatabaseもいいけど...

Laravel で DB に関するテストをする際は RefreshDatabase を使うのがテッパンだと思います( ドキュメント )。

しかし、 RefreshDatabase はテストが終わったらマイグレーションを再度実行するというものなので、 マイグレーションを使わない場合 (例えば、既存 DB を使う場合)には適していません。1

そこでDatabaseTransactionsを使おう

Illuminate\Foundation\Testing\DatabaseTransactions は、 テストを実行しているあいだだけ有効なトランザクション を作ってくれるトレイトです。具体的には、 setUp() したタイミングでトランザクションを作成し、 tearDown() したタイミングでロールバックします。2

実は、Laravel 5.4ドキュメント には記載があるのですが、 5.5RefreshDatabase が追加されたからか、ドキュメントからは削除されてしまったようです。

1つ注意しないといけないのは、 DatabaseTransactions は、何も設定しなければ、デフォルトの DB 接続 3 に対してトランザクションを作るということです。

もし、デフォルト以外の DB 接続に対してトランザクションを作りたい場合は、テストクラスにプロパティ $connectionsToTransact を作り、 config/database.php に設定した DB 接続名を指定することができます。

Tests\Unit\SampleServiceTest
namespace Tests\Unit;

use Tests\TestCase;
use App\Http\Services\SampleService;
use Illuminate\Support\Facades\DB;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class SampleServiceTest extends TestCase
{
    // テスト実行中のみ有効なトランザクションを設定する。
    use DatabaseTransactions;

    // トランザクションを設定する DB 接続名を指定する。
    private $connectionsToTransact = [ 'mysql_slave' ];

    public function setUp(): void
    {
        parent::setUp();

        // トランザクションを設定した DB に対して、テストデータを INSERT する。
        DB::connection('mysql_slave')->table('d_products')
            ->insert([
                [
                    'name' => 'りんご',
                ],
                [
                    'name' => 'オレンジ',
                ],
        ]);
    }

    /**
     * @test
     */
    public function 製品一覧を取得できること()
    {
        $service = new SampleService();
        $result = $service->fetchProducts();

        $expected = [
            (object)['name' => 'りんご'],
            (object)['name' => 'オレンジ'],
        ];

        $this->assertEquals($expected, $result);
    }
}

注意点

Laravel 5.4 で手軽にテストを書こう!laravelでDBテストコードを書く前の設定すべきこと で指摘されているように、 AUTO_INCREMENT の設定値は変わってしまいます。

終わりに

RefreshDatabase しか無いのか... と軽く絶望していたときに、 DatabaseTransactions を見つけたときは、本当に嬉しかったです。はい。

  1. そりゃ、私だって、あんな メチャクチャな テーブル定義の DB を使いたくはないですよ。どうせ、Laravel で作るなら、心機一転、全部作り直したい。でも、 大人の事情 という例のアレのせいで、そうできないんですよ!(溜息)

  2. Illuminate\Foundation\Testing\TestCasesetUpTraits()DatabaseTransactions クラスを使っている場合は、その beginDatabaseTransaction() をコールしています。 beginDatabaseTransaction() では、プロパティ $beforeApplicationDestroyedCallbacks にロールバックと DB 接続を切断する関数を追加しています。ここで追加された関数が tearDown() でコールされます。

  3. config/database.phpdefault の設定値を参照します。 .envDB_CONNECTION が設定されていれば、それが優先されます。設定されていなければ、 'mysql' が使われます。

30
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?