Skip to content
赞助商
虚位以待
赞助商
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待

数据库测试

介绍

Laravel 提供了多种有用的工具,使测试数据库驱动的应用程序变得更容易。首先,您可以使用 assertDatabaseHas 辅助函数来断言数据库中存在与给定条件匹配的数据。例如,如果您想验证 users 表中是否存在 email 值为 sally@example.com 的记录,可以执行以下操作:

php
public function testDatabase()
{
    // 调用应用程序...

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

您还可以使用 assertDatabaseMissing 辅助函数来断言数据库中不存在数据。

assertDatabaseHas 方法和其他类似的辅助函数是为了方便。您可以自由使用 PHPUnit 的任何内置断言方法来补充您的功能测试。

生成工厂

要创建工厂,请使用 make:factory Artisan 命令

php
php artisan make:factory PostFactory

新工厂将被放置在您的 database/factories 目录中。

--model 选项可用于指示工厂创建的模型的名称。此选项将预填充生成的工厂文件中的给定模型:

php
php artisan make:factory PostFactory --model=Post

在每个测试后重置数据库

在每个测试后重置数据库通常是有用的,以防止前一个测试的数据干扰后续测试。RefreshDatabase trait 根据您使用的是内存数据库还是传统数据库,采用最优的方法迁移您的测试数据库。在您的测试类中使用该 trait,一切都将为您处理:

php
<?php

namespace Tests\Feature;

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

class ExampleTest extends TestCase
{
    use RefreshDatabase;

    /**
     * 一个基本的功能测试示例。
     *
     * @return void
     */
    public function testBasicExample()
    {
        $response = $this->get('/');

        // ...
    }
}

编写工厂

在测试时,您可能需要在执行测试之前向数据库插入一些记录。与手动指定每个列的值以创建测试数据不同,Laravel 允许您为每个 Eloquent 模型 定义一组默认属性。要开始,请查看应用程序中的 database/factories/UserFactory.php 文件。默认情况下,此文件包含一个工厂定义:

php
use Faker\Generator as Faker;
use Illuminate\Support\Str;

$factory->define(App\User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'email_verified_at' => now(),
        'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // 密码
        'remember_token' => Str::random(10),
    ];
});

在作为工厂定义的闭包中,您可以返回模型上所有属性的默认测试值。闭包将接收 Faker PHP 库的一个实例,该库允许您方便地生成各种类型的随机数据以进行测试。

您还可以为每个模型创建额外的工厂文件以便更好地组织。例如,您可以在 database/factories 目录中创建 UserFactory.phpCommentFactory.php 文件。factories 目录中的所有文件将自动由 Laravel 加载。

NOTE

您可以通过在 config/app.php 配置文件中添加 faker_locale 选项来设置 Faker 的区域设置。

扩展工厂

如果您扩展了一个模型,您可能希望也扩展其工厂,以便在测试和播种期间利用子模型的工厂属性。为此,您可以调用工厂构建器的 raw 方法,从任何给定的工厂中获取原始属性数组:

php
$factory->define(App\Admin::class, function (Faker\Generator $faker) {
    return factory(App\User::class)->raw([
        // ...
    ]);
});

工厂状态

状态允许您定义可以以任何组合应用于模型工厂的离散修改。例如,您的 User 模型可能有一个 delinquent 状态,该状态修改其默认属性值之一。您可以使用 state 方法定义状态转换。对于简单的状态,您可以传递一个属性修改数组:

php
$factory->state(App\User::class, 'delinquent', [
    'account_status' => 'delinquent',
]);

如果您的状态需要计算或 $faker 实例,您可以使用闭包来计算状态的属性修改:

php
$factory->state(App\User::class, 'address', function ($faker) {
    return [
        'address' => $faker->address,
    ];
});

工厂回调

工厂回调使用 afterMakingafterCreating 方法注册,允许您在制作或创建模型后执行其他任务。例如,您可以使用回调将其他模型关联到创建的模型:

php
$factory->afterMaking(App\User::class, function ($user, $faker) {
    // ...
});

$factory->afterCreating(App\User::class, function ($user, $faker) {
    $user->accounts()->save(factory(App\Account::class)->make());
});

您还可以为 工厂状态 定义回调:

php
$factory->afterMakingState(App\User::class, 'delinquent', function ($user, $faker) {
    // ...
});

$factory->afterCreatingState(App\User::class, 'delinquent', function ($user, $faker) {
    // ...
});

使用工厂

创建模型

定义工厂后,您可以在功能测试或种子文件中使用全局 factory 函数生成模型实例。让我们看几个创建模型的示例。首先,我们将使用 make 方法创建模型,但不将其保存到数据库:

php
public function testDatabase()
{
    $user = factory(App\User::class)->make();

    // 在测试中使用模型...
}

您还可以创建多个模型的集合或创建给定类型的模型:

php
// 创建三个 App\User 实例...
$users = factory(App\User::class, 3)->make();

应用状态

您还可以将任何 状态 应用于模型。如果您想将多个状态转换应用于模型,您应该指定要应用的每个状态的名称:

php
$users = factory(App\User::class, 5)->states('delinquent')->make();

$users = factory(App\User::class, 5)->states('premium', 'delinquent')->make();

覆盖属性

如果您想覆盖模型的一些默认值,可以将一个值数组传递给 make 方法。只有指定的值会被替换,而其余的值将保持为工厂指定的默认值:

php
$user = factory(App\User::class)->make([
    'name' => 'Abigail',
]);

NOTE

使用工厂创建模型时,批量赋值保护 会自动禁用。

持久化模型

create 方法不仅创建模型实例,还使用 Eloquent 的 save 方法将其保存到数据库:

php
public function testDatabase()
{
    // 创建一个 App\User 实例...
    $user = factory(App\User::class)->create();

    // 创建三个 App\User 实例...
    $users = factory(App\User::class, 3)->create();

    // 在测试中使用模型...
}

您可以通过传递一个数组给 create 方法来覆盖模型上的属性:

php
$user = factory(App\User::class)->create([
    'name' => 'Abigail',
]);

关系

在此示例中,我们将为一些创建的模型附加一个关系。使用 create 方法创建多个模型时,将返回一个 Eloquent 集合实例,允许您使用集合提供的任何便捷函数,例如 each

php
$users = factory(App\User::class, 3)
           ->create()
           ->each(function ($user) {
                $user->posts()->save(factory(App\Post::class)->make());
            });

您可以使用 createMany 方法创建多个相关模型:

php
$user->posts()->createMany(
    factory(App\Post::class, 3)->make()->toArray()
);

关系和属性闭包

您还可以在工厂定义中将关系附加到模型。例如,如果您想在创建 Post 时创建一个新的 User 实例,可以执行以下操作:

php
$factory->define(App\Post::class, function ($faker) {
    return [
        'title' => $faker->title,
        'content' => $faker->paragraph,
        'user_id' => factory(App\User::class),
    ];
});

如果关系依赖于定义它的工厂,您可以提供一个接受评估属性数组的回调:

php
$factory->define(App\Post::class, function ($faker) {
    return [
        'title' => $faker->title,
        'content' => $faker->paragraph,
        'user_id' => factory(App\User::class),
        'user_type' => function (array $post) {
            return App\User::find($post['user_id'])->type;
        },
    ];
});

使用种子

如果您想在功能测试期间使用 数据库播种器 填充数据库,可以使用 seed 方法。默认情况下,seed 方法将返回 DatabaseSeeder,它应该执行所有其他播种器。或者,您可以将特定的播种器类名传递给 seed 方法:

php
<?php

namespace Tests\Feature;

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

class ExampleTest extends TestCase
{
    use RefreshDatabase;

    /**
     * 测试创建新订单。
     *
     * @return void
     */
    public function testCreatingANewOrder()
    {
        // 运行 DatabaseSeeder...
        $this->seed();

        // 运行单个播种器...
        $this->seed(OrderStatusSeeder::class);

        // ...
    }
}

可用的断言

Laravel 为您的 PHPUnit 功能测试提供了几个数据库断言:

方法描述
$this->assertDatabaseCount($table, int $count);断言数据库中的表包含给定数量的条目。
$this->assertDatabaseHas($table, array $data);断言数据库中的表包含给定数据。
$this->assertDatabaseMissing($table, array $data);断言数据库中的表不包含给定数据。
$this->assertDeleted($table, array $data);断言给定记录已被删除。
$this->assertSoftDeleted($table, array $data);断言给定记录已被软删除。

为了方便起见,您可以将模型传递给 assertDeletedassertSoftDeleted 辅助函数,以根据模型的主键断言记录已从数据库中删除或软删除。

例如,如果您在测试中使用模型工厂,可以将此模型传递给这些辅助函数之一,以测试您的应用程序是否正确删除了数据库中的记录:

php
public function testDatabase()
{
    $user = factory(App\User::class)->create();

    // 调用应用程序...

    $this->assertDeleted($user);
}