> ## 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` 会判断数据库结构是否为最新版本。如果是最新的，就使用事务运行测试；否则会先执行迁移。

```mermaid theme={null}
flowchart TD
    A[使用 RefreshDatabase 的测试开始] --> B{数据库结构是否为最新?}
    B -- 是 --> C[在事务中执行]
    B -- 否 --> D[执行迁移]
    D --> E[执行测试]
    C --> E[执行测试]
    E --> F[回滚 / 重置状态]
```

如果不使用事务方式，希望每次都完全重置，可以使用以下 trait。

| Trait                | 行为                 | 使用场景               |
| -------------------- | ------------------ | ------------------ |
| `RefreshDatabase`    | 结构最新时用事务，仅在必要时执行迁移 | 通常首选               |
| `DatabaseMigrations` | 每个测试都执行一次迁移        | 需要每次都走完整的迁移生命周期    |
| `DatabaseTruncation` | 通过 TRUNCATE 表来初始化  | 不适合用事务的环境下需要彻底清空数据 |

## 模型工厂

在测试前准备数据时，使用模型工厂可以简洁地生成 Eloquent 模型。

工厂的定义、state 和关联关系细节请参考 [Eloquent 工厂](/zh/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，可以在基类测试上添加 `#[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`

断言指定的模型（或模型集合）存在于数据库中。

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

断言指定的模型（或模型集合）不存在于数据库中。

<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/advanced/package-testing.md)
- [HTTP 测试](/zh/http-tests.md)
- [数据库配置](/zh/database.md)
- [数据库填充（Seeding）](/zh/seeding.md)
- [测试入门](/zh/testing.md)
