数据库测试
介绍
Laravel 提供了各种有用的工具和断言,使测试数据库驱动的应用程序更加容易。此外,Laravel 模型工厂和 Seeders 可以轻松地使用应用程序的 Eloquent 模型和关系创建测试数据库记录。我们将在下面的文档中讨论所有这些强大的功能。
每次测试后重置数据库
在继续进行之前,让我们讨论如何在每个测试之后重置数据库,以便前一个测试的数据不会干扰后续测试。Laravel 包含的 TraitIlluminate\Foundation\Testing\RefreshDatabase
将为你解决这一问题。只需在测试类上使用这个 Trait:
<?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 test_basic_example()
{
$response = $this->get('/');
// ...
}
}
如果你的数据库模式(Schema)是最新的,那么这个 TraitIlluminate\Foundation\Testing\RefreshDatabase
并不会迁移数据库。相反,它将只在一个数据库事务中执行测试。因此,任何由测试用例添加到数据库的记录,如果不使用这个 Trait,可能仍然存在于数据库中。
如果你想使用迁移来完全重置数据库,可以使用 Trait Illuminate\Foundation\Testing\DatabaseMigrations
来代替。然而,DatabaseMigrations
Trait 明显比 RefreshDatabase
Trait 慢。
定义模型工厂
概念概述
首先,让我们谈谈 Eloquent 模型工厂。测试时,你可能需要在执行测试之前向数据库中插入一些记录。 Laravel 允许你使用模型工厂为每个 Eloquent 模型 定义一组默认属性,而不是在创建测试数据时手动指定每一列的值。
要了解如何编写工厂的示例,请查看应用程序中的 database/factories/UserFactory.php
文件。这个工厂包含在所有新的 Laravel 源码程序中,并包含以下工厂定义:
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class UserFactory extends Factory
{
/**
* 定义模型的默认值。
*
* @return array
*/
public function definition()
{
return [
'name' => $this->faker->name,
'email' => $this->faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
}
}
正如你所见,在最基本的形式中,factories 是继承 Laravel 的基础 factory 类和定义 definition
方法的类。definition
方法返回使用 factory 创建模型时应用的默认属性值集合。
通过 faker
属性, factories 可以访问 Faker PHP 函数库,它允许你便捷的生成各种随机数据来进行测试。
技巧:你也可以在 config/app.php
配置文件中添加 faker_locale
选项来设置 Faker 的语言环境。
创建工厂
要创建工厂,请使用 Artisan 命令 make:factory
:
php artisan make:factory PostFactory
新工厂将放置在你的 database/factories
目录下。
模型和工厂的关联约定
定义工厂后,可以在模型中使用 Illuminate\Database\Eloquent\Factories\HasFactory
特性提供的 factory
静态方法,来为模型实例化工厂。
HasFactory
特性的 factory
方法将按约定来为模型确定合适的工厂。具体来说,该方法将在 Database\Factorys
的命名空间下查找类名与模型名相匹配,并以 Factory
为后缀的工厂。如果当前约定不适用于你的特定应用程序或工厂,你可以重写模型中的 newFactory
方法,返回模型实际对应的工厂实例:
use Database\Factories\Administration\FlightFactory;
/**
* 为当前模型创建一个工厂实例
*
* @return \Illuminate\Database\Eloquent\Factories\Factory
*/
protected static function newFactory()
{
return FlightFactory::new();
}
接下来,在对应的工厂中定义 model
属性:
use App\Administration\Flight;
use Illuminate\Database\Eloquent\Factories\Factory;
class FlightFactory extends Factory
{
/**
* 工厂对应的模型名称
*
* @var string
*/
protected $model = Flight::class;
}
工厂状态
你可以定义各自独立的状态操作方法,并可以任意组合应用于你的模型工厂。例如,你的 Database\Factories\UserFactory
工厂可能包含修改其默认属性值的 suspended
状态方法
状态转换方法通常会调用 Laravel 的基础工厂类提供的 state
方法 。 state
方法接收一个闭包,该闭包将收到工厂的原始属性数组,并应该返回要修改的属性数组:
/**
* 标识用户已停用
*
* @return \Illuminate\Database\Eloquent\Factories\Factory
*/
public function suspended()
{
return $this->state(function (array $attributes) {
return [
'account_status' => 'suspended',
];
});
}
工厂回调
工厂回调是通过 afterMaking
和 afterCreating
方法来注册的,并且允许你在创建模型之后执行其他任务。 你应该通过在工厂类上定义 configure
方法来注册这些回调。 实例化工厂后,Laravel 将自动调用此方法:
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class UserFactory extends Factory
{
/**
* 配置模型工厂
*
* @return $this
*/
public function configure()
{
return $this->afterMaking(function (User $user) {
//
})->afterCreating(function (User $user) {
//
});
}
// ...
}
使用工厂创建模型
实例化模型
一旦你定义了工厂,就可以使用 Illuminate\Database\Eloquent\Factories\HasFactory
特性为你的模型提供的 factory
静态方法来实例化工厂。让我们来看几个创建模型的例子。首先,我们将使用 make
方法来创建模型而且不需要将它们持久化到数据库中:
use App\Models\User;
public function test_models_can_be_instantiated()
{
$user = User::factory()->make();
// 在测试中使用模型...
}
你可以使用 count
方法创建许多模型的集合:
$users = User::factory()->count(3)->make();
应用各种状态
你也可以应用你的任何一个 states 到模型. 如果你想向模型应用多个状态转换,则可以直接调用状态转换方法:
$users = User::factory()->count(5)->suspended()->make();
覆盖属性
如果你想覆盖模型的一些默认值, 你可以将数组传递给make
方法. 只有指定的属性将被替换,而这些属性的其余部分保持设置为其默认值,则为出厂指定:
$user = User::factory()->make([
'name' => 'Abigail Otwell',
]);