0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Laravelでテスト後にデータベースをリセットする方法

Last updated at Posted at 2024-01-07

はじめに

Laravelでテストを書いていて、テストメソッド同士が互いに影響し合ってしまうケースがありました。

具体的には、別のメソッドで作成したテストデータが、同クラス内の他のメソッドの実行時にも残ってしまっていました。

そんな時は、LaravelのRefreshDatabaseクラスを使用します。

環境

Laravel 9.52.16

問題

  • 以下のようなテストを実装した
PurchaseOrdersControllerTest.php
<?php 

namespace Tests\Feature;

use App\Models\User;
use Tests\TestCase;

class PurchaseOrdersControllerTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */

    // テストとしてmy/purchase/storeにアクセスできるかどうかを確認
    public function test_purchase_store(): void
    {
        // テスト用のユーザを作成
        $user = User::factory()->create();

        // テスト用のフォームデータを作成
        $formData = [
            'title' => 'テストタイトル',
            'purchase_abstract' => 'テスト概要',
            'category' => 'テストカテゴリー',
            'budget' => 1000,
            'schedule' => '2021-01-01',
        ];

        // 作成したユーザでアクセス
        $response = $this->actingAs($user)->post('/my/purchase/store', $formData);

        $response->assertStatus(302);
        $this->assertDatabaseHas('purchase_orders', [
            'title' => $formData['title'],
            'purchase_abstract' => $formData['purchase_abstract'],
            'category' => $formData['category'],
            'budget' => $formData['budget'],
            'schedule' => $formData['schedule'],
        ]);
    }

    // テストとしてsales/destroy/{PurchaseId}にアクセスできるかどうかを確認
    public function test_purchase_destroy(): void
    {
        // テスト用のユーザを作成
        $user = User::factory()->create();

        // テスト用のフォームデータを作成
        $formData = [
            'title' => 'テストタイトル',
            'purchase_abstract' => 'テスト概要',
            'category' => 'テストカテゴリー',
            'budget' => 1000,
            'schedule' => '2021-01-01',
        ];

        // 作成したユーザでフォームデータを保存
        $response = $this->actingAs($user)->post('/my/purchase/store', $formData);

        $response->assertStatus(302);
        $this->assertDatabaseHas('purchase_orders', [
            'title' => $formData['title'],
            'purchase_abstract' => $formData['purchase_abstract'],
            'category' => $formData['category'],
            'budget' => $formData['budget'],
            'schedule' => $formData['schedule'],
        ]);

        // 作成したフォームデータを取得
        $purchase_order = $user->purchase_orders()->first();

        // 作成したフォームデータを削除
        $response = $this->actingAs($user)->delete('/purchase/destroy/' . $purchase_order->id);

        $response->assertStatus(302);
        $this->assertDatabaseMissing('purchase_orders', [
            'title' => $formData['title'],
            'purchase_abstract' => $formData['purchase_abstract'],
            'category' => $formData['category'],
            'budget' => $formData['budget'],
            'schedule' => $formData['schedule'],
        ]);
    }
}
  • このテストを実行すると、次のようなエラーが出る
ターミナル
root@:/var/www/html/niches# php artisan test --filter PurchaseOrdersControllerTest

   FAIL  Tests\Feature\PurchaseOrdersControllerTest
   purchase store
   purchase destroy

  ---

   Tests\Feature\PurchaseOrdersControllerTest > purchase destroy
  Failed asserting that a row in the table [purchase_orders] does not match the attributes {
      "title": "テストタイトル",
      "purchase_abstract": "テスト概要",
      "category": "テストカテゴリー",
      "budget": 1000,
      "schedule": "2021-01-01"
  }.

  Found similar results: [
      {
          "title": "テストタイトル",
          "purchase_abstract": "テスト概要",
          "category": "テストカテゴリー",
          "budget": "1000.00",
          "schedule": "2021-01-01"
      }
  ].

test_purchase_destroy()により、同メソッド内で保存したデータが削除され、テストが無事パスされることを期待していました。

しかしエラーによると、「データがまだ残っている」とのことでした。

解決法

  • test_purchase_store()内のデータが削除されていないため、エラーが出ていた
  • そのためRefreshDatabaseを使用し、テストメソッドが実行された後にデータベースをリセットする
  • これにより、以前のテストデータが後続のテストに干渉しないようになる

RefreshDatabaseを使う方法は簡単です。

まず名前空間をインポートし、そしてテストクラス内で「use RefreshDatabase」と記述してトレイトの機能を組み込みます。

PurchaseOrdersControllerTest.php(解決案)
<?php 

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase; // ここで
use App\Models\User;
use Tests\TestCase;

class PurchaseOrdersControllerTest extends TestCase
{
    use RefreshDatabase;
    
    /**
     * A basic test example.
     *
     * @return void
     */

    // テストとしてmy/purchase/storeにアクセスできるかどうかを確認
    public function test_purchase_store(): void
    {
        // 以下略
}
root@:/var/www/html/niches# php artisan test --filter PurchaseOrdersControllerTest

   PASS  Tests\Feature\PurchaseOrdersControllerTest
   purchase store
   purchase destroy

  Tests:  2 passed
  Time:   13.13s

おわりに

「トレイト」という単語も今回初めて知りました。

これは継承と同じくメソッドなどを再利用するために使われるようです。

ただしトレイトはクラスではないため、これを継承したりインスタンス化することはできません。

また、単独で使用することもできません。

管見の限りでは、トレイトの定義に関する記述を公式ドキュメントで見つけることができなかったので、

自分の理解が間違っていればご指摘いただけると助かります…!!

参考

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?