LaravelでのMigration/Seederを使ったDB試験の話

Laravelの自動テストにおいてDBを使った試験はどうしますか？
DBを使った自動テストは作っていなませんという人も多いでしょう。
今回はMigrateとSeederを使いDBを使った試験を実現します。

# migrateとseederとは
migrationでは定義ファイル通りにスキーマを作成します。
seederでは作成された空のスキーマにデータを入れます
ということです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2481837/fa410a91-e97f-0674-207a-7105c1a4b735.png)


# migrationとは
マイグレーションはデータベースのバージョン管理のようなものです。
データベースのスキーマ定義し共有することができるようになります。
ソース管理から変更を取り込んだ後、ローカルデータベースのスキーマにカラムを追加するよう指示があるなら、差分を更新されます。
その差分管理を行うにはマイグレーションが問題を解決するでしょう。

参考 : https://laravel.com/docs/9.x/migrations
DDLとは異なりDBの種別(mysql,postgresなど)方言がなくなります。

database/migrationsに定義を保存して、ファイル名順にスキーマ変更を指定します
番号とかは必要ですのでmake:migrateコマンドから枠を作る方が良いです

```bash
$ php artisan make:migrate create_book
```
記述内容… (略)

https://github.com/wataru775/learning-leravel/blob/20220421_seeder_test/database/migrations/2022_04_21_010614_create_books.php

# Seederとは
Laravelには、シードクラスを使用して、データベースにデータをシードする機能があります。
すべてのシードクラスは、database/seedersディレクトリに格納されています。
デフォルトでは、DatabaseSeederクラスが定義されています。
このクラスから、callメソッドを使用して他のシードクラスを実行することができ、シードの順番を制御することができます。

https://laravel.com/docs/9.x/seeding
```bash
$ php artisan make:seeder books
```
https://github.com/wataru775/learning-leravel/blob/20220421_seeder_test/database/seeders/BooksSeeder.php

# migrationとseederを用いると試験はどうなるか
migrationとseederを用いることにより試験毎にDBを再構成します。
それにより試験は専用のDBを作ることができます。
専用DBに一定のデータを投入することにより、自ずとロジックの結果は一定になります。
それを評価すれば自動テストにできるわけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2481837/4b0b41d9-f374-295a-e0c6-dfec6a6820af.png)

データを更新した場合にはDBやファイルなどでは毎回初期化する必要があります。
メモリ上に作ることにより毎回初期化しても気にもなりません。

解説 : [LaravelでSQLiteをメモリ上で動かす話(2022年4月26日)](https://www.mmpp.org/archives/2826)


DBを更新する試験は更新後の内容を直接覗くことで試験が可能です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2481837/ac7e5daa-f1f4-634a-91af-eaad6f000321.png)


# migrateとSeederを使う方法
試験時にmigrateとSeederをどの様に実行すればいいのかを説明します

初めにDBの作成タイミングを考えます。
テスト毎回DBを再構成するならsetup
テストケースにて共用のDB内容を定義するならsetupBeforeClasを利用します。
該当箇所でartisan migrateとseederを実行すればDBでできます。

他のタイミングは別記事にまとめております。
[PHPUnitでの試験前後の実行タイミングの話(2022年4月26日)](https://www.mmpp.org/archives/2839)

今回はテスト開始タイミングで毎回再構成を行いますのでsetupにしています

```bash
public function setup(): void
{
    parent::setUp();
    $this->artisan('migrate');
    $this->seed('BooksSeeder');
}
```
これによりartsan migrateを実行されますので、 database/migrations 内の書籍情報を実行されます。
構成されるDBの抜粋は [2022_04_21_010614_create_books.php](https://github.com/wataru775/learning-leravel/blob/20220421_seeder_test/database/migrations/2022_04_21_010614_create_books.php)です。
実行されるとbooksテーブルが作成されます

次にBooksSeederを指定することにより、database/seeders内の[BooksSeeder.php](https://github.com/wataru775/learning-leravel/blob/20220421_seeder_test/database/seeders/BooksSeeder.php)が実行されてDB内容が入ります。
書籍のタイトルが適当に入ります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2481837/e2e2ec9d-4662-2aa3-b61e-0e4a7e1b4d36.png)


# Seederのデータを覗けるかだけの試験
初めにSeederがちゃんと入っているだけを確認する試験をサンプルに作成しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2481837/d2ac0ad8-34e6-e92c-42c3-7b1cc95a7728.png)


サンプルソース

試験 : [SeederTest.php](https://github.com/wataru775/learning-leravel/blob/20220421_seeder_test/tests/Unit/SeederTest.php)
Model : [Book.php](https://github.com/wataru775/learning-leravel/blob/20220421_seeder_test/app/Classes/Book.php)
データ取得はModelのfindにプライマリーキーを入れれば取得できます

```php
$book = Book::find(1);
$this->assertNotNull($book,'データが取れること');
$this->assertEquals(1 , $book->id , 'データが取れること');
$this->assertEquals('ドメイン特化言語 パターンで学ぶDSLのベストプラクティス46項目' , $book->title , 'データが取れること');
```
これにより、最初に挿入した書籍のタイトルが取れれば試験で合格です…

# サービスの試験
先ほどの試験はModelの動作試験レベルだったのでもう少し実際の試験っぽいサンプルを作ります。

今回のサービスは、書籍のプライマリキーを引き渡すと、書籍タイトルを「」で括った値が返ってくるサービスメソッド serveTitleを試験します。

処理はこんだけです。（抜粋です。全試験は全実装はこちら : [BooksService.php](https://github.com/wataru775/learning-leravel/blob/20220421_seeder_test/app/Services/BooksService.php)）

```php
public function serveTitle(int $id) : ?string{
    $book = BookModel::find($id);
    $title = ' 「 ' . $book->title . ' 」 ';
    return $title;
}
```
試験は以下の様に書けば想定の試験になります。 ([BooksServiceTest.php](https://github.com/wataru775/learning-leravel/blob/20220421_seeder_test/tests/Unit/BooksServiceTest.php))

```php
// サービスを作成します
$booksService = new BooksService();
 
$title = $booksService->serveTitle(1);
$this->assertEquals(' 「 ドメイン特化言語 パターンで学ぶDSLのベストプラクティス46項目 」 ' , $title);
先ほど1番で取った書籍名称に「」を括っただけです
```

フラグを下げると、括弧はなくなります

```php
$title = $booksService->serveTitle(1 , false);
$this->assertEquals('ドメイン特化言語 パターンで学ぶDSLのベストプラクティス46項目' , $title);
```
この試験によりロジック上でModelからDBからデータを取り出して、Serviceで何らかの処理を行えることが確認できました。

# 終いに
今回はLaravelにてDBを用いた試験の方法をざっくり説明しました。

これでかなり本番に違い試験ができる様になるでしょうね。

今回は大きな書籍情報BooksSeederの例を出しました。
しかし、ExampleTestSeederなどとこの試験用などと愚考します。
共用とした場合には下手にいじって別試験が落ちるなどが発生しないでしょうね。
個人的にはBooksSeederはFuture / 試験名称でのSeederは Unitとかルールを決めると良いですね。

# 参照
プロジェクト : https://github.com/wataru775/learning-leravel/tree/20220421_seeder_test