数据库测试
数据库测试
简介
Laravel提供了各种有用的工具,可以更轻松地测试数据库驱动的应用程序。 首先, 你可以使用 assertDatabaseHas
辅助函数,来断言数据库中是否存在与指定条件互相匹配的数据。 例如,如果我们想验证 users
数据表中是否存在 email
值为 sally@example.com
的数据, 可按如下方式操作:
public function testDatabase()
{
// 调用应用程序...
$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
trait 会采用最优的方法来迁移你的数据库,这取决于你使用的是内存数据库还是传统数据库。在你的测试类引用这个trait,一切将为你处理:
<?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 Faker\Generator as Faker;
$factory->define(App\User::class, function (Faker $faker) {
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
'remember_token' => str_random(10),
];
});
在这个模型工厂定义的闭包中,你可以返回模型上所有属性的默认测试值。闭包将接受 PHP 函数库 Faker 的一个实例,它允许你便捷的生成各种随机数据来进行测试。
你也可以为每个模型创建一个工厂文件以实现更好的组织。 例如,你可以自database/factories
目录下创建 UserFactory.php
和CommentFactory.php
文件。 Laravel将自动加载 factories
目录下的所有文件。
{tip} 你也可以在
config/app.php
配置文件中添加faker_locale
选项来设置 Faker 的语言环境。
工厂状态
工厂状态可以让你任意组合你的模型工厂,仅需要做出适当差异化的修改,就可以达到让模型拥有多种不同的状态。例如, 你的 User
模型中可以修改某个默认属性值来达到标识一种 delinquent
状态。你可以使用 state
方法来进行这种状态转换。对于简单的工厂状态,你可以传入要修改的属性数组:
$factory->state(App\User::class, 'delinquent', [
'account_status' => 'delinquent',
]);
如果你的工厂状态需要计算或者使用 $faker
实例。你可以使用闭包方法来实现状态属性的修改:
$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();
应用状态
你也可将任何状态应用于模型。若将多个状态转换应用于模型,你应当为每个状态指定名称:
$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
方法创建模型实例的同时还调用了把记录写入数据库的 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 ($u) {
$u->posts()->save(factory(App\Post::class)->make());
});
关联 & 属性闭包
模型工厂定义时,也可以使用闭包里的属性来给模型添加关联。比如,创建 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); |
断言数据库中的指定记录已软删除。 |