ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
* * * * * [TOC] ## 简介 Laravel提供了各种有用的工具,以便更容易地测试数据库驱动的应用程序。 首先, 你可以使用 `assertDatabaseHas`辅助函数, 来断言数据库中是否存在与指定条件互相匹配的数据。 例如,如果我们想验证 `users` 数据表中是否存在 `email` 值为 `sally@example.com`的数据, 你可以按照以下方式来测试: ~~~ public function testDatabase() { // 创建调用至应用程序... $this->assertDatabaseHas('users', [ 'email' => 'sally@example.com' ]); } ~~~ 你也可以使用 `assertDatabaseMissing` 辅助函数来断言数据不在此数据库中。 当然,使用 `assertDatabaseHas` 方法和其它辅助函数只是为了方便。 你也可以随意使用PHPUnit内置的断言方法来扩充测试。 ## 生成模型工厂 创建一个模型工厂, 使用 Artisan 命令 `make:factory` 来快速创建模型工厂: ~~~ 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模型](https://www.kancloud.cn/tonyyu/laravel_5_6/786272)的工厂来设置每个属性的默认值。在开始之前, 你可以先看一下应用程序 `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](https://github.com/fzaninotto/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, ]; }); ~~~ ## 使用模型工厂 ### 创建模型 模型工厂定义后,就可以在测试或者是数据库的填充文件中,通过全局的 `factory` 函数来生成模型实例。因此,先让我们来看看几个模型创建的例子。 首先,我们将使用 `make` 方法创建模型但不将他们保存至数据库: ~~~ public function testDatabase() { $user = factory(App\User::class)->make(); // 在测试中使用模型... } ~~~ 你也可以创建一个含有多个模型的集合,或创建一个指定类型的模型: ~~~ // 创建三个 App\User 实例... $users = factory(App\User::class, 3)->make(); ~~~ #### 应用模型工厂状态 你可能需要在你的模型中应用不同的 [模型工厂状态](https://www.kancloud.cn/tonyyu/laravel_5_6/786284#_91) 。 如果你想模型加上多种不同的状态,你只需指定每个你想要添加状态的名称就可以了: ~~~ $users = factory(App\User::class, 5)->states('delinquent')->make(); $users = factory(App\User::class, 5)->states('premium', 'delinquent')->make(); ~~~ ### 使用回调 你可以使用 `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()); }); ~~~ 你可以可以为 [模型工厂状态](https://www.kancloud.cn/tonyyu/laravel_5_6/786284#_91) 定义回调: ~~~ $factory->afterMakingState(App\User::class, 'delinquent', function ($user, $faker) { // ... }); $factory->afterCreatingState(App\User::class, 'delinquent', function ($user, $faker) { // ... }); ~~~ #### 重写模型属性 通过给 `make` 方法传入一个数组,可以重写模型的默认属性值。这样仅替换指定的属性,其他的属性还是由模型工厂默认填充: ~~~ $user = factory(App\User::class)->make([ 'name' => 'Abigail', ]); ~~~ ### 模型持久化 `create` 方法创建模型实例的同时还调用了把记录写入数据库的 `save` 方法: ~~~ public function testDatabase() { // 创建单个 App\User 实例 $user = factory(App\User::class)->create(); // 创建三个 App\User 实例 $users = factory(App\User::class, 3)->create(); // 在测试中使用模型 } ~~~ 传一个数组给 `create` 方法重写模型的属性: ~~~ $user = factory(App\User::class)->create([ 'name' => 'Abigail', ]); ~~~ ### 关联 这个例子,将为模型添加关联。`create` 方法创建多模型时,返回 Eloquent [实例集合](https://www.kancloud.cn/tonyyu/laravel_5_6/786274),这样就可以在集合上使用 `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](https://phpunit.de/) 测试提供了多个数据库断言方法: | 方法 | 描述 | | --- | --- | | `$this->assertDatabaseHas($table, array $data);` | 断言数据库表中含有指定数据。 | | `$this->assertDatabaseMissing($table, array $data);` | 断言数据库表中不含有指定数据。 | | `$this->assertSoftDeleted($table, array $data);` | 断言数据库中的指定记录已软删除。 |