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 Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;

class ExampleTest extends TestCase
{
    use RefreshDatabase;

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

        // ...
    }
}

编写工厂

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

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

$factory->define(App\User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'email_verified_at' => now(),
        'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
        'remember_token' => Str::random(10),
    ];
});

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

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

NOTE

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

工厂状态

状态允许您定义可以以任何组合应用于模型工厂的离散修改。例如,您的 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' => function () {
            return factory(App\User::class)->create()->id;
        }
    ];
});

这些闭包还接收定义它们的工厂的已评估属性数组:

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

使用种子

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

php
<?php

namespace Tests\Feature;

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

class ExampleTest extends TestCase
{
    use RefreshDatabase;

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

        // 运行单个种子...
        $this->seed(OrderStatusesTableSeeder::class);

        // ...
    }
}

可用的断言

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

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