数据库测试
Database Testing
简介
Laravel 提供了各种有用的工具,可以更轻松地测试数据库驱动的应用程序。 首先,您可以使用 assertDatabaseHas
帮助程序断言数据库中存在的数据与给定的一组条件匹配。 例如,如果您要验证 users
表中的记录是否具有 sally @ example.com
的 email
值,您可以执行以下操作:
public function testDatabase()
{
// Make call to application...
$this->assertDatabaseHas('users', [
'email' => 'sally@example.com'
]);
}
您还可以使用 assertDatabaseMissing
帮助程序断言数据库中不存在数据。
assertDatabaseHas
方法和其他类似的帮助程序是为了方便起见。 您可以自由使用任何 PHPUnit 的内置断言方法来补充您的测试。
生成模型工厂
使用 make:factory
Artisan command 命令可以创建一个模型工厂:
php artisan make:factory PostFactory
新生成的工厂位置在 database/factories
目录下。
--model
选项可用于指示工厂创建的模型的名称。 此选项将使用给定模型预填充生成的工厂文件:
php artisan make:factory PostFactory --model=Post
每次测试后重置数据库
在每次测试后重置数据库通常很有用,这样前一次测试的数据不会干扰后续测试。 RefreshDatabase
特征采用最优化的方法来迁移测试数据库,具体取决于您使用的是内存数据库还是传统数据库。 在测试类上使用特征,一切都将为您处理:
<?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
文件。 开箱即用,此文件包含一个工厂定义:
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),
];
});
在用作工厂定义的 Closure 中,您可以返回模型上所有属性的默认测试值。 Closure 将收到 Faker PHP 库的一个实例,它允许您方便地生成各种随机数据以进行测试。
您还可以为每个模型创建其他工厂文件,以便更好地进行组织。 例如,您可以在 database / factories
目录中创建 UserFactory.php
和 CommentFactory.php
文件。 factories
目录中的所有文件都将由 Laravel 自动加载。
您可以通过在
config / app.php
配置文件中添加faker_locale
选项来设置 Faker 的语言环境。
工厂状态
States 允许您定义可以任意组合应用于模型工厂的离散修改。 例如,您的 User
模型可能具有 deinquent
状态,可以修改其默认属性值之一。 您可以使用 state
方法定义状态转换。 对于简单状态,您可以传递一组属性修改:
$factory->state(App\User::class, 'delinquent', [
'account_status' => 'delinquent',
]);
如果你的状态需要计算或 $ faker
实例,你可以使用 Closure 来计算状态的属性修改:
$factory->state(App\User::class, 'address', function ($faker) {
return [
'address' => $faker->address,
];
});
工厂回调
使用 afterMaking
和 afterCreating
方法注册工厂回调,并允许您在创建或创建模型后执行其他任务。 例如,您可以使用回调将其他模型与创建的模型相关联:
$factory->afterMaking(App\User::class, function ($user, $faker) {
// ...
});
$factory->afterCreating(App\User::class, function ($user, $faker) {
$user->accounts()->save(factory(App\Account::class)->make());
});
您还可以为 工厂状态 定义回调:
$factory->afterMakingState(App\User::class, 'delinquent', function ($user, $faker) {
// ...
});
$factory->afterCreatingState(App\User::class, 'delinquent', function ($user, $faker) {
// ...
});
使用模型工厂
创建模型
模型工厂定义后,就可以在测试或种子文件中使用全局 factory
函数来生成模型实例。 那么,让我们来看一些创建模型的例子。 首先,我们将使用 make
方法创建模型,但不将它们保存到数据库中:
public function testDatabase()
{
$user = factory(App\User::class)->make();
//在测试中使用模型...
}
您还可以创建许多模型的集合或创建给定类型的模型:
//创建三个 App\User 实例...
$users = factory(App\User::class, 3)->make();
应用状态
您也可以将任何 states 应用于模型。 如果要将多个状态转换应用于模型,则应指定要应用的每个状态的名称:
$users = factory(App\User::class, 5)->states('delinquent')->make();
$users = factory(App\User::class, 5)->states('premium', 'delinquent')->make();
覆盖属性
如果要覆盖模型的某些默认值,可以将一组值传递给 make
方法。 只有指定的值才会被替换,而其余值仍然设置为工厂指定的默认值:
$user = factory(App\User::class)->make([
'name' => 'Abigail',
]);
持久化模型
create
方法不仅创建了模型实例,还使用Eloquent的 save
方法将它们保存到数据库中:
public function testDatabase()
{
// 创建单个 App\User 实例...
$user = factory(App\User::class)->create();
// 创建3个 App\User 实例..
$users = factory(App\User::class, 3)->create();
// 在测试中使用模型...
}
您可以通过将数组传递给 create
方法来覆盖模型上的属性:
$user = factory(App\User::class)->create([
'name' => 'Abigail',
]);
关联
在这个例子中,我们将附加一些创建模型的关系。 当使用 create
方法创建多个模型时,返回一个 Eloquent 集合实例 ,这样就可以在集合上使用 each 等便利方法:
$users = factory(App\User::class, 3)
->create()
->each(function ($user) {
$user->posts()->save(factory(App\Post::class)->make());
});
关联 & 属性闭包
您还可以使用工厂定义中的 Closure 属性将关系附加到模型。 例如,如果您想在创建 Post
时创建一个新的 User
实例,您可以执行以下操作:
$factory->define(App\Post::class, function ($faker) {
return [
'title' => $faker->title,
'content' => $faker->paragraph,
'user_id' => function () {
return factory(App\User::class)->create()->id;
}
];
});
这些闭包接收一个包含工厂属性的数组:
$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;
}
];
});
可用的断言方法
Laravel 为 PHPUnit 测试提供了多个数据库断言方法:
方法 | 描述 |
---|---|
$this->assertDatabaseHas($table, array $data); |
断言数据库表中包含给定的数据。 |
$this->assertDatabaseMissing($table, array $data); |
断言数据库表中不包含给定的数据。 |
$this->assertSoftDeleted($table, array $data); |
断言数据库中的指定记录已软删除。 |