laravel
9. 测试
数据库测试

介绍

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

在每次测试后重置数据库

在进一步深入之前,让我们讨论如何在每次测试后重置您的数据库,以便前一个测试的数据不会干扰后续测试。Laravel 包含的 Illuminate\Foundation\Testing\RefreshDatabase 特征将为您处理此问题。只需在您的测试类中使用该特征:

<?php
 
use Illuminate\Foundation\Testing\RefreshDatabase;
 
uses(RefreshDatabase::class);
 
test('基本示例', function () {
    $response = $this->get('/');
 
    //...
});
<?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('/');
 
        //...
    }
}

如果您的模式是最新的,Illuminate\Foundation\Testing\RefreshDatabase 特征不会迁移您的数据库。相反,它只会在数据库事务中执行测试。因此,由未使用此特征的测试用例添加到数据库中的任何记录可能仍然存在于数据库中。

如果您想要完全重置数据库,可以使用 Illuminate\Foundation\Testing\DatabaseMigrationsIlluminate\Foundation\Testing\DatabaseTruncation 特征来代替。然而,这两个选项都比 RefreshDatabase 特征慢得多。

模型工厂

在测试时,在执行测试之前,您可能需要向数据库中插入一些记录。Laravel 允许您使用模型工厂为每个Eloquent 模型定义一组默认属性,而不是在创建此测试数据时手动指定每个列的值。

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

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方法:

<?php
 
use Database\Seeders\OrderStatusSeeder;
use Database\Seeders\TransactionStatusSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
 
uses(RefreshDatabase::class);
 
test('orders can be created', function () {
    // 运行 DatabaseSeeder...
    $this->seed();
 
    // 运行特定的种子器...
    $this->seed(OrderStatusSeeder::class);
 
    //...
 
    // 运行特定种子器的数组...
    $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;
 
    /**
     * 测试创建新订单。
     */
    public function test_orders_can_be_created(): void
    {
        // 运行 DatabaseSeeder...
        $this->seed();
 
        // 运行特定的种子器...
        $this->seed(OrderStatusSeeder::class);
 
        //...
 
        // 运行特定种子器的数组...
        $this->seed([
            OrderStatusSeeder::class,
            TransactionStatusSeeder::class,
            //...
        ]);
    }
}

或者,您可以指示 Laravel 在使用RefreshDatabase特征的每个测试之前自动为数据库播种。您可以通过在基础测试类上定义$seed属性来实现此目的:

<?php

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 为您的 Pest (opens in a new tab)PHPUnit (opens in a new tab) 功能测试提供了几个数据库断言。我们将在下面讨论这些断言中的每一个。

assertDatabaseCount

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

$this->assertDatabaseCount('users', 5);  // 此断言用于检查'users'表中是否有 5 条记录

assertDatabaseHas

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

$this->assertDatabaseHas('users', [
    'email' => 'sally@example.com',
]);  // 此断言用于检查'users'表中是否存在'email'字段值为'sally@example.com'的记录

assertDatabaseMissing

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

$this->assertDatabaseMissing('users', [
    'email' => 'sally@example.com',
]);  // 此断言用于检查'users'表中是否不存在'email'字段值为'sally@example.com'的记录

assertSoftDeleted

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

$this->assertSoftDeleted($user);  // 此断言用于检查给定的 $user 模型是否已被软删除

assertNotSoftDeleted

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

$this->assertNotSoftDeleted($user);  // 此断言用于检查给定的 $user 模型是否未被软删除

assertModelExists

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

use App\Models\User;

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

$this->assertModelExists($user);  // 此断言用于检查给定的 $user 模型是否存在于数据库中

assertModelMissing

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

use App\Models\User;

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

$user->delete();

$this->assertModelMissing($user);  // 此断言用于检查给定的 $user 模型是否不存在于数据库中

expectsDatabaseQueryCount

expectsDatabaseQueryCount 方法可以在测试的开头被调用,以指定您期望在测试期间运行的数据库查询的总数。如果实际执行的查询数量与这个期望不完全匹配,测试将失败:

$this->expectsDatabaseQueryCount(5);  // 此断言用于指定测试中期望运行的数据库查询总数为 5

// 测试...