数据库测试

简介

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

每次测试后重置数据库

在进一步讨论之前,让我们先讨论一下如何在每个测试后重置数据库,以确保之前测试的数据不会干扰后续的测试。Laravel 提供的 Illuminate\Foundation\Testing\RefreshDatabase 特性可以为你处理这个问题。只需在测试类中使用该特性:

  • Pest

  • PHPUnit

<?php

use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

test('basic example', function () {
    $response = $this->get('/');

    // ...
});
<?php

namespace Tests\Feature;

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

class ExampleTest extends TestCase
{
    use RefreshDatabase;

    /**
     * A basic functional test example.
     */
    public function test_basic_example(): void
    {
        $response = $this->get('/');

        // ...
    }
}

Illuminate\Foundation\Testing\RefreshDatabase 特性不会在数据库模式已经是最新时迁移数据库。它只会在数据库事务中执行测试。因此,任何没有使用此特性的测试案例所添加的记录可能仍然存在于数据库中。

如果你希望完全重置数据库,可以使用 Illuminate\Foundation\Testing\DatabaseMigrationsIlluminate\Foundation\Testing\DatabaseTruncation 特性。不过,这两种选项的速度明显比 RefreshDatabase 特性慢。

模型工厂

在测试时,你可能需要在执行测试之前将一些记录插入到数据库中。Laravel 允许你为每个 【Eloquent 模型】定义一组默认属性,通过【模型工厂】来创建这些测试数据,而无需手动指定每个列的值。

要了解更多关于创建和使用模型工厂来创建模型的内容,请参阅完整的【模型工厂文档】。定义好模型工厂后,你可以在测试中使用工厂来创建模型:

  • Pest

  • PHPUnit

use App\Models\User;

test('models can be instantiated', function () {
    $user = User::factory()->create();

    // ...
});
use App\Models\User;

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

    // ...
}

运行数据填充器

如果你希望在功能测试期间使用【数据库填充器】来填充数据库,可以调用 seed 方法。默认情况下,seed 方法将执行 DatabaseSeeder,该种子类应执行所有其它的填充器。或者,你可以将特定的填充器类名称传递给 seed 方法:

  • Pest

  • PHPUnit

<?php

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

uses(RefreshDatabase::class);

test('orders can be created', function () {
    // Run the DatabaseSeeder...
    $this->seed();

    // Run a specific seeder...
    $this->seed(OrderStatusSeeder::class);

    // ...

    // Run an array of specific seeders...
    $this->seed([
        OrderStatusSeeder::class,
        TransactionStatusSeeder::class,
        // ...
    ]);
});
<?php

namespace Tests\Feature;

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

class ExampleTest extends TestCase
{
    use RefreshDatabase;

    /**
     * Test creating a new order.
     */
    public function test_orders_can_be_created(): void
    {
        // Run the DatabaseSeeder...
        $this->seed();

        // Run a specific seeder...
        $this->seed(OrderStatusSeeder::class);

        // ...

        // Run an array of specific seeders...
        $this->seed([
            OrderStatusSeeder::class,
            TransactionStatusSeeder::class,
            // ...
        ]);
    }
}

另外,你可以指示 Laravel 在每个使用 RefreshDatabase 特性的测试之前自动填充数据库。你可以通过在基础测试类中定义 $seed 属性来实现这一点:

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    /**
     * 指示在每个测试之前是否应该运行默认填充器。
     *
     * @var bool
     */
    protected $seed = true;
}

$seed 属性为 true 时,测试将在每个使用 RefreshDatabase 特性的测试之前运行 Database\Seeders\DatabaseSeeder 类。你也可以通过在测试类中定义 $seeder 属性来指定应该执行的特定填充器:

use Database\Seeders\OrderStatusSeeder;

/**
 * 在每个测试之前运行特定的填充器。
 *
 * @var string
 */
protected $seeder = OrderStatusSeeder::class;

可用断言

Laravel 提供了几种数据库断言,可以用于你的 PestPHPUnit 功能测试。以下是每个断言的详细说明:

assertDatabaseCount

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

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

assertDatabaseEmpty

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

$this->assertDatabaseEmpty('users');

assertDatabaseHas

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

$this->assertDatabaseHas('users', [
    'email' => 'sally@example.com',
]);

assertDatabaseMissing

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

$this->assertDatabaseMissing('users', [
    'email' => 'sally@example.com',
]);

assertSoftDeleted

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

$this->assertSoftDeleted($user);

assertNotSoftDeleted

assertNotSoftDeleted 方法用于断言给定的 Eloquent 模型没有被“软删除”:

$this->assertNotSoftDeleted($user);

assertModelExists

断言给定的模型存在于数据库中:

use App\Models\User;

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

$this->assertModelExists($user);

assertModelMissing

断言给定的模型不存在于数据库中:

use App\Models\User;

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

$user->delete();

$this->assertModelMissing($user);

expectsDatabaseQueryCount

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

$this->expectsDatabaseQueryCount(5);

// 测试...