> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# 資料庫測試

> 如何運用 RefreshDatabase、Seeder、Factory 與專屬斷言，有效率地撰寫 Laravel 的資料庫測試

## 簡介

Laravel 內建了各種能安全又快速驗證使用資料庫功能的機制。結合資料庫重置 trait、模型工廠、Seeder 與專屬斷言，你可以寫出不易損壞的測試。

首要建議是使用 `RefreshDatabase`。在多數情況下，它比完整重置更能兼顧測試獨立性與速度。

## 每次測試後重置資料庫

為避免前一個測試的資料影響下一個測試，可使用 Laravel 的資料庫重置 trait。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  use Illuminate\Foundation\Testing\RefreshDatabase;

  uses(RefreshDatabase::class);

  test('基本範例', function () {
      $response = $this->get('/');

      $response->assertOk();
  });
  ```

  ```php PHPUnit theme={null}
  <?php

  namespace Tests\Feature;

  use Illuminate\Foundation\Testing\RefreshDatabase;
  use Tests\TestCase;

  class ExampleTest extends TestCase
  {
      use RefreshDatabase;

      public function test_basic_example(): void
      {
          $response = $this->get('/');

          $response->assertOk();
      }
  }
  ```
</CodeGroup>

`RefreshDatabase` 會判斷 schema 是否為最新。若為最新則以 transaction 執行測試，否則會先執行 migration。

```mermaid theme={null}
flowchart TD
    A[使用 RefreshDatabase 的測試開始] --> B{schema 是否最新？}
    B -- 是 --> C[以 DB transaction 執行]
    B -- 否 --> D[執行 migration]
    D --> E[執行測試]
    C --> E[執行測試]
    E --> F[rollback / 狀態重置]
```

若不想使用 transaction 方式，希望每次徹底重置時，可使用下列 trait。

| Trait                | 行為                                          | 適用時機                      |
| -------------------- | ------------------------------------------- | ------------------------- |
| `RefreshDatabase`    | schema 為最新時使用 transaction，僅在必要時執行 migration | 通常會作為首選                   |
| `DatabaseMigrations` | 每個測試都執行 migration                           | 想每次都走完 migration 完整生命週期時  |
| `DatabaseTruncation` | 以 TRUNCATE 清空資料表                            | transaction 不適用時、需清空所有資料時 |

## 模型工廠

在測試前準備資料，可以用模型工廠簡潔地建立 Eloquent 模型。

Factory 定義、state、關聯的細節請參考 [Eloquent 工廠](/zh-TW/eloquent-factories)。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  use App\Models\User;

  test('可以建立模型', function () {
      $user = User::factory()->create();

      expect($user->exists)->toBeTrue();
  });
  ```

  ```php PHPUnit theme={null}
  <?php

  namespace Tests\Feature;

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

  class UserFactoryTest extends TestCase
  {
      public function test_models_can_be_instantiated(): void
      {
          $user = User::factory()->create();

          $this->assertTrue($user->exists);
      }
  }
  ```
</CodeGroup>

## 執行 Seeder

若想在測試中匯入資料，可使用 `seed()`。無參數時執行 `DatabaseSeeder`，有參數時可執行特定 Seeder（或陣列）。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  use Database\Seeders\OrderStatusSeeder;
  use Database\Seeders\TransactionStatusSeeder;
  use Illuminate\Foundation\Testing\RefreshDatabase;

  uses(RefreshDatabase::class);

  test('可以建立訂單', function () {
      $this->seed();

      $this->seed(OrderStatusSeeder::class);

      $this->seed([
          OrderStatusSeeder::class,
          TransactionStatusSeeder::class,
      ]);

      // ...
  });
  ```

  ```php PHPUnit theme={null}
  <?php

  namespace Tests\Feature;

  use Database\Seeders\OrderStatusSeeder;
  use Database\Seeders\TransactionStatusSeeder;
  use Illuminate\Foundation\Testing\RefreshDatabase;
  use Tests\TestCase;

  class OrderTest extends TestCase
  {
      use RefreshDatabase;

      public function test_orders_can_be_created(): void
      {
          $this->seed();

          $this->seed(OrderStatusSeeder::class);

          $this->seed([
              OrderStatusSeeder::class,
              TransactionStatusSeeder::class,
          ]);

          // ...
      }
  }
  ```
</CodeGroup>

若希望使用 `RefreshDatabase` 的所有測試每次都自動 seed，可為基底 TestCase 加上 `#[Seed]` 屬性。

```php theme={null}
<?php

namespace Tests;

use Illuminate\Foundation\Testing\Attributes\Seed;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

#[Seed]
abstract class TestCase extends BaseTestCase
{
}
```

若只想自動執行特定 Seeder，可使用 `#[Seeder(...)]`。

```php theme={null}
<?php

namespace Tests\Feature;

use Database\Seeders\OrderStatusSeeder;
use Illuminate\Foundation\Testing\Attributes\Seeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

#[Seeder(OrderStatusSeeder::class)]
class OrderStatusTest extends TestCase
{
    use RefreshDatabase;
}
```

## 可用的斷言

Laravel 提供多種可在 Pest / PHPUnit 中共用的資料庫專屬斷言。

### `assertDatabaseCount`

確認資料表中的紀錄筆數與預期一致。

<CodeGroup>
  ```php Pest theme={null}
  $this->assertDatabaseCount('users', 5);
  ```

  ```php PHPUnit theme={null}
  $this->assertDatabaseCount('users', 5);
  ```
</CodeGroup>

### `assertDatabaseEmpty`

確認資料表為空。

<CodeGroup>
  ```php Pest theme={null}
  $this->assertDatabaseEmpty('users');
  ```

  ```php PHPUnit theme={null}
  $this->assertDatabaseEmpty('users');
  ```
</CodeGroup>

### `assertDatabaseHas`

確認存在符合指定條件的紀錄。

<CodeGroup>
  ```php Pest theme={null}
  $this->assertDatabaseHas('users', [
      'email' => 'sally@example.com',
  ]);
  ```

  ```php PHPUnit theme={null}
  $this->assertDatabaseHas('users', [
      'email' => 'sally@example.com',
  ]);
  ```
</CodeGroup>

### `assertDatabaseMissing`

確認不存在符合指定條件的紀錄。

<CodeGroup>
  ```php Pest theme={null}
  $this->assertDatabaseMissing('users', [
      'email' => 'sally@example.com',
  ]);
  ```

  ```php PHPUnit theme={null}
  $this->assertDatabaseMissing('users', [
      'email' => 'sally@example.com',
  ]);
  ```
</CodeGroup>

### `assertSoftDeleted`

確認目標 Eloquent 模型已被軟刪除。

<CodeGroup>
  ```php Pest theme={null}
  $this->assertSoftDeleted($user);
  ```

  ```php PHPUnit theme={null}
  $this->assertSoftDeleted($user);
  ```
</CodeGroup>

### `assertNotSoftDeleted`

確認目標 Eloquent 模型尚未被軟刪除。

<CodeGroup>
  ```php Pest theme={null}
  $this->assertNotSoftDeleted($user);
  ```

  ```php PHPUnit theme={null}
  $this->assertNotSoftDeleted($user);
  ```
</CodeGroup>

### `assertModelExists`

確認指定模型（或模型集合）在 DB 中存在。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  use App\Models\User;

  $user = User::factory()->create();

  $this->assertModelExists($user);
  ```

  ```php PHPUnit theme={null}
  <?php

  use App\Models\User;

  $user = User::factory()->create();

  $this->assertModelExists($user);
  ```
</CodeGroup>

### `assertModelMissing`

確認指定模型（或模型集合）在 DB 中不存在。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  use App\Models\User;

  $user = User::factory()->create();
  $user->delete();

  $this->assertModelMissing($user);
  ```

  ```php PHPUnit theme={null}
  <?php

  use App\Models\User;

  $user = User::factory()->create();
  $user->delete();

  $this->assertModelMissing($user);
  ```
</CodeGroup>

### `expectsDatabaseQueryCount`

於測試開頭嚴格指定所執行的資料庫查詢總數。若實際執行次數與預期不符則失敗。

<CodeGroup>
  ```php Pest theme={null}
  $this->expectsDatabaseQueryCount(5);

  // Test...
  ```

  ```php PHPUnit theme={null}
  $this->expectsDatabaseQueryCount(5);

  // Test...
  ```
</CodeGroup>


## Related topics

- [以 Orchestra Testbench 測試 Laravel 套件](/zh-TW/advanced/package-testing.md)
- [資料庫 Seeding](/zh-TW/seeding.md)
- [HTTP 測試](/zh-TW/http-tests.md)
- [資料庫設定](/zh-TW/database.md)
- [測試入門](/zh-TW/testing.md)
