【Laravel】インメモリSqliteを使ってDB周りのテストコードを書く

November 20, 2021
Laravel / Unit Test / Docker / PHP

業務で、インメモリSqliteを使ってローカル開発環境のDBが汚れることなくテストコードを実行できるようにしたので、その備忘録です。

先日LaravelのDocker環境を構築した記事を作成しましたが、同じリポジトリ`にテストコードをpushしました。

Image

上記今回対応したコミット3つです。

やってみたこと

  1. notesテーブルにdeleted_atカラムを追加する。
  2. 論理削除フラグとして利用するためにNoteクラスに設定を記述する。
  3. 「Noteモデルからデータの詳細や一覧を取得する際に、削除済のデータは自動的に除外してくれることを確認する。」ためのテストコードを書く。

では、詳細を記載していきます。

1. notesテーブルにdeleted_atカラムを追加する

コミットはこちらです。ポイントは$table->softDeletes();の部分。これでマイグレーションコマンド実行すると、Timesamp型でdelete_atを追加してくれます。

    public function up()
    {
        Schema::table('notes', function (Blueprint $table) {
          $table->softDeletes(); // deleted_atを追加します
        });
    }

マイグレーションコマンドはphp artisan migrateです↓docker環境に入って実行します。

$ docker-compose exec php bash
$ cd /var/www/laravel && php artisan migrate

2. Noteクラスにソフトデリート(論理削除)の設定を記述する

コミットはこちら

3. 「Noteモデルからデータの詳細や一覧を取得する際に、削除済のデータは自動的に除外してくれることを確認する。」ためのテストコードを書く。

コミットはこちら

まず、database.phpに以下追記します。

        'sqlite_testing' => [
          'driver' => 'sqlite',
          'database' => ':memory:',
          'prefix' => '',
        ],

次に、phpunit.xmlに以下追記します

 <server name="DB_CONNECTION" value="sqlite_testing"/>

これだけでインメモリSqliteを使ってDB周りのテストコードを各準備が整いました。ローカル開発環境のDBは汚したくない、というニーズをこれで満たせます。

今回書いたテストコードを貼り付けます。

<?php

namespace Tests\Unit;

use Tests\TestCase;
use App\Models\Note;

class NoteTest extends TestCase
{
    public function setUp(): void
    {
      parent::setUp();

      // マイグレーション実行
      $this->artisan('migrate');

      // テストデータ投入 seeder使うのもあり
      Note::factory()->create([
        'id' => 1,
        'title' => '削除したメモです',
        'deleted_at' => '2021-11-20 09:50:00', // 削除済データ
      ]);
      Note::factory()->create([
        'id' => 2,
        'title' => '生きてるメモです',
      ]);
    }

    /**
     * @return void
     */
    public function test_論理削除フラグONのデータがnullであること()
    {
      $result = Note::find(1);
      $this->assertNull($result);
    }

    /**
     * @return void
     */
    public function test_論理削除フラグOFFのデータがnullでないこと()
    {
      $result = Note::find(2);
      $this->assertNotNull($result);
    }

    /**
     * @return void
     */
    public function test_論理削除フラグONのデータが一覧に含まれないこと()
    {
      $result = Note::all()->pluck('id');
      $expected = Note::hydrate([
        [
          'id' => 2,
          'title' => '生きてるメモです',
        ],
      ])->pluck('id');
      $this->assertEquals($result, $expected);
    }
}

ポイントとしては、

setUp()の中で、マイグレーション・テストデータを投入ているところです。setUp()は個別のテストメソッドが実行される前に実行されるので事前処理をしておくメソッドです。Seederを使ってテストデータを投入するのも全然ありですが、今回はひと目見てすぐテストデータがどんなデータなのかをイメージしやすいようにsetUp()の中でFactoryを使ってつらつら記載してます。

id = 1のデータはdeleted_atに値を入れて削除済データとして投入し、id = 2のデータは生きているデータとして投入しました。

テストメソッド名は、「test」という文字列がメソッド名に含まれていれば日本語でもOKです。今回は以下3つのテストメソッドを作成しました。

test_論理削除フラグONのデータがnullであること()

test_論理削除フラグOFFのデータがnullでないこと()

test_論理削除フラグONのデータが一覧に含まれないこと()

これでテストコードを実行すると、こんな感じで結果が表示されます↓

Image

テストコードを実行するコマンドは、こちらです↓テスト対象のファイルを書かない場合は全てのテストが実行されます。

$ docker-compose exec php bash
$ cd /var/www/laravel
$ .vendor/bin/phpunit {テスト対象のファイル}

余談ですが、mysqlで動くのにsqliteでは動かないといったトラブルはありそうです。現に、mysql のMODIFYはsqlliteでは使えず、一部のマイグレーションがエラーになる事象に業務で遭遇しました。その場合はマイグレーションは実行せずに、愚直に必要なテーブルだけを作成する作戦でなんとか回避しました・・・(下記)。テストで必要なテーブルが少なかったこと、またテーブル定義が本番DBと揃っていなくてもテストには影響しなかったことが幸いでした。

// 全マイグレーションの中の、必要な部分だけピックアップして実行する
(new CreateNotesTable())->up();
(new AddDeletedAtOnNotes())->up();

以上です。


Profile picture

React, Vue, TypeScript, Node.js, PHP, Laravel, AWS, Firebase, Docker, GitHub Actions, etc...