跳转至内容

数据库测试

简介

Laravel 提供了多种实用的工具和断言,使测试数据库驱动的应用程序变得更加容易。此外,Laravel 的模型工厂(Model Factories)和数据填充器(Seeders)可以让你使用应用程序的 Eloquent 模型和关联关系轻松创建测试数据库记录。我们将在本文档中讨论所有这些强大的功能。

在每次测试后重置数据库

在继续深入之前,让我们先讨论如何在每次测试后重置数据库,以确保上一次测试的数据不会干扰后续测试。Laravel 内置的 Illuminate\Foundation\Testing\RefreshDatabase trait 会为你处理好这一切。只需在你的测试类中使用该 trait 即可。

1<?php
2 
3use Illuminate\Foundation\Testing\RefreshDatabase;
4 
5pest()->use(RefreshDatabase::class);
6 
7test('basic example', function () {
8 $response = $this->get('/');
9 
10 // ...
11});
1<?php
2 
3namespace Tests\Feature;
4 
5use Illuminate\Foundation\Testing\RefreshDatabase;
6use Tests\TestCase;
7 
8class ExampleTest extends TestCase
9{
10 use RefreshDatabase;
11 
12 /**
13 * A basic functional test example.
14 */
15 public function test_basic_example(): void
16 {
17 $response = $this->get('/');
18 
19 // ...
20 }
21}

如果你的架构(Schema)已是最新,Illuminate\Foundation\Testing\RefreshDatabase trait 不会重新迁移数据库。相反,它仅会在数据库事务内执行测试。因此,未显式使用此 trait 的测试用例所添加的记录可能会残留于数据库中。

如果你希望彻底重置数据库,可以使用 Illuminate\Foundation\Testing\DatabaseMigrationsIlluminate\Foundation\Testing\DatabaseTruncation trait 代替。不过,这两种方案的执行速度都比 RefreshDatabase trait 慢得多。

模型工厂

在进行测试时,你可能需要在执行测试前向数据库插入几条记录。与其在创建测试数据时手动指定每一列的值,Laravel 允许你使用 模型工厂 为你的 Eloquent 模型 定义一组默认属性。

要了解有关创建和利用模型工厂来生成模型的更多信息,请参阅完整的 模型工厂文档。一旦定义了模型工厂,你就可以在测试中使用该工厂来创建模型。

1use App\Models\User;
2 
3test('models can be instantiated', function () {
4 $user = User::factory()->create();
5 
6 // ...
7});
1use App\Models\User;
2 
3public function test_models_can_be_instantiated(): void
4{
5 $user = User::factory()->create();
6 
7 // ...
8}

运行数据填充器

如果你希望在功能测试期间使用 数据库填充器 来填充数据库,可以调用 seed 方法。默认情况下,seed 方法会执行 DatabaseSeeder,该类应包含你所有其他填充器的执行逻辑。或者,你也可以将特定的填充器类名传递给 seed 方法。

1<?php
2 
3use Database\Seeders\OrderStatusSeeder;
4use Database\Seeders\TransactionStatusSeeder;
5use Illuminate\Foundation\Testing\RefreshDatabase;
6 
7pest()->use(RefreshDatabase::class);
8 
9test('orders can be created', function () {
10 // Run the DatabaseSeeder...
11 $this->seed();
12 
13 // Run a specific seeder...
14 $this->seed(OrderStatusSeeder::class);
15 
16 // ...
17 
18 // Run an array of specific seeders...
19 $this->seed([
20 OrderStatusSeeder::class,
21 TransactionStatusSeeder::class,
22 // ...
23 ]);
24});
1<?php
2 
3namespace Tests\Feature;
4 
5use Database\Seeders\OrderStatusSeeder;
6use Database\Seeders\TransactionStatusSeeder;
7use Illuminate\Foundation\Testing\RefreshDatabase;
8use Tests\TestCase;
9 
10class ExampleTest extends TestCase
11{
12 use RefreshDatabase;
13 
14 /**
15 * Test creating a new order.
16 */
17 public function test_orders_can_be_created(): void
18 {
19 // Run the DatabaseSeeder...
20 $this->seed();
21 
22 // Run a specific seeder...
23 $this->seed(OrderStatusSeeder::class);
24 
25 // ...
26 
27 // Run an array of specific seeders...
28 $this->seed([
29 OrderStatusSeeder::class,
30 TransactionStatusSeeder::class,
31 // ...
32 ]);
33 }
34}

或者,你也可以指示 Laravel 在每个使用 RefreshDatabase trait 的测试之前自动填充数据库。通过在你的基础测试类上添加 Seed 属性即可实现这一点。

1<?php
2 
3namespace Tests;
4 
5use Illuminate\Foundation\Testing\Attributes\Seed;
6use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
7 
8#[Seed]
9abstract class TestCase extends BaseTestCase
10{
11}

Seed 属性存在时,测试会在每个使用 RefreshDatabase trait 的测试之前运行 Database\Seeders\DatabaseSeeder 类。此外,你也可以通过在测试类上使用 Seeder 属性来指定需要执行的特定填充器。

1<?php
2 
3namespace Tests\Feature;
4 
5use Database\Seeders\OrderStatusSeeder;
6use Illuminate\Foundation\Testing\Attributes\Seeder;
7use Illuminate\Foundation\Testing\RefreshDatabase;
8use Tests\TestCase;
9 
10#[Seeder(OrderStatusSeeder::class)]
11class OrderTest extends TestCase
12{
13 use RefreshDatabase;
14 
15 // ...
16}

可用断言

Laravel 为你的 PestPHPUnit 功能测试提供了多种数据库断言。我们将在下面讨论这些断言。

assertDatabaseCount

断言数据库中的某个表包含给定数量的记录。

1$this->assertDatabaseCount('users', 5);

assertDatabaseEmpty

断言数据库中的某个表不包含任何记录。

1$this->assertDatabaseEmpty('users');

assertDatabaseHas

断言数据库中的某个表包含匹配给定键/值查询约束的记录。

1$this->assertDatabaseHas('users', [
2 'email' => '[email protected]',
3]);

assertDatabaseMissing

断言数据库中的某个表不包含匹配给定键/值查询约束的记录。

1$this->assertDatabaseMissing('users', [
2 'email' => '[email protected]',
3]);

assertSoftDeleted

assertSoftDeleted 方法可用于断言给定的 Eloquent 模型已被“软删除”。

1$this->assertSoftDeleted($user);

assertNotSoftDeleted

assertNotSoftDeleted 方法可用于断言给定的 Eloquent 模型未被“软删除”。

1$this->assertNotSoftDeleted($user);

assertModelExists

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

1use App\Models\User;
2 
3$user = User::factory()->create();
4 
5$this->assertModelExists($user);

assertModelMissing

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

1use App\Models\User;
2 
3$user = User::factory()->create();
4 
5$user->delete();
6 
7$this->assertModelMissing($user);

expectsDatabaseQueryCount

可以在测试开始时调用 expectsDatabaseQueryCount 方法,以指定测试期间预期的数据库查询总数。如果实际执行的查询数量与此预期不完全匹配,测试将会失败。

1$this->expectsDatabaseQueryCount(5);
2 
3// Test...