生成测试数据

通常,您需要应用程序的示例数据才能运行其测试。 Fabricator 类使用 fzaninotto 的 Faker 将模型转换为随机数据的生成器。 在您的种子或测试用例中使用 fabricator 为您的单元测试准备虚假数据。

支持的模型

Fabricator 支持任何扩展框架核心模型 CodeIgniter\Model 的模型。 您可以通过确保您的自定义模型实现 CodeIgniter\Test\Interfaces\FabricatorModel 来使用它们。

<?php

namespace App\Models;

use CodeIgniter\Test\Interfaces\FabricatorModel;

class MyModel implements FabricatorModel
{
    // ...
}

注意

除了方法之外,该接口还概述了目标模型的一些必要属性。 请参阅接口代码以了解详细信息。

加载 Fabricator

最基本的是,fabricator 会获取要操作的模型

<?php

use App\Models\UserModel;
use CodeIgniter\Test\Fabricator;

$fabricator = new Fabricator(UserModel::class);

参数可以是指定模型名称的字符串,也可以是模型本身的实例

<?php

use App\Models\UserModel;
use CodeIgniter\Test\Fabricator;

$model = new UserModel($testDbConnection);

$fabricator = new Fabricator($model);

定义格式化器

Faker 通过向格式化器请求数据来生成数据。如果没有定义格式化器,Fabricator 将尝试根据字段名称和它所代表的模型的属性来猜测最合适的匹配项,并回退到 $fabricator->defaultFormatter。如果您的字段名称与常见的格式化器相对应,或者您不太关心字段的内容,这可能没问题,但大多数情况下,您需要将要使用的格式化器指定为构造函数的第二个参数

<?php

use App\Models\UserModel;
use CodeIgniter\Test\Fabricator;

$formatters = [
    'first'  => 'firstName',
    'email'  => 'email',
    'phone'  => 'phoneNumber',
    'avatar' => 'imageUrl',
];

$fabricator = new Fabricator(UserModel::class, $formatters);

您也可以在初始化 Fabricator 后使用 setFormatters() 方法更改格式化器。

高级格式化

有时格式化器的默认返回值不够。Faker 提供程序允许对大多数格式化器使用参数来进一步限制随机数据的范围。Fabricator 将检查其代表模型的 fake() 方法,您可以在其中定义伪造数据应该是什么样子

<?php

namespace App\Models;

class UserModel
{
    // ...

    public function fake(Generator &$faker)
    {
        return [
            'first'  => $faker->firstName,
            'email'  => $faker->email,
            'phone'  => $faker->phoneNumber,
            'avatar' => Faker\Provider\Image::imageUrl(800, 400),
            'login'  => config('Auth')->allowRemembering ? date('Y-m-d') : null,
        ];

        /*
         * Or you can return a return type object.

        return new User([
            'first'  => $faker->firstName,
            'email'  => $faker->email,
            'phone'  => $faker->phoneNumber,
            'avatar' => Faker\Provider\Image::imageUrl(800, 400),
            'login'  => config('Auth')->allowRemembering ? date('Y-m-d') : null,
        ]);

        */
    }
}

请注意,在此示例中,前三个值等同于之前的格式化器。但是,对于 avatar,我们请求了与默认值不同的图像大小,而 login 使用了基于应用程序配置的条件,这两种情况都无法使用 $formatters 参数实现。您可能希望将测试数据与生产模型分开,因此在您的测试支持文件夹中定义一个子类是一个好习惯

<?php

namespace Tests\Support\Models;

use App\Models\UserModel;

class UserFabricator extends UserModel
{
    public function fake(&$faker)
    {
        // ...
    }
}

本地化

Faker 支持多种不同的语言环境。请查看其文档以确定哪些提供程序支持您的语言环境。在初始化 Fabricator 时,在第三个参数中指定语言环境。

<?php

use App\Models\UserModel;
use CodeIgniter\Test\Fabricator;

$fabricator = new Fabricator(UserModel::class, null, 'fr_FR');

如果没有指定语言环境,它将使用在 **app/Config/App.php** 中定义的 defaultLocale 语言环境。您可以使用 Fabricator 的 getLocale() 方法检查现有 Fabricator 的语言环境。

伪造数据

一旦您拥有一个正确初始化的 Fabricator,就可以使用 make() 命令轻松生成测试数据。

<?php

use CodeIgniter\Test\Fabricator;

$fabricator = new Fabricator(\UserFabricator::class);
$testUser   = $fabricator->make();
print_r($testUser);

您可能会得到类似这样的结果。

<?php

[
    'first'  => 'Maynard',
    'email'  => '[email protected]',
    'phone'  => '201-886-0269 x3767',
    'avatar' => 'http://lorempixel.com/800/400/',
    'login'  => null,
];

您还可以通过提供一个计数来获取大量数据。

<?php

$users = $fabricator->make(10);

make() 的返回值模仿了代表模型中定义的内容,但您可以使用直接方法强制类型。

<?php

$userArray  = $fabricator->makeArray();
$userObject = $fabricator->makeObject();
$userEntity = $fabricator->makeObject('App\Entities\User');

make() 的返回值可以用于测试或插入数据库。或者,Fabricator 包含 create() 命令来为您插入它并返回结果。由于模型回调、数据库格式化以及主键和时间戳等特殊键,create() 的返回值可能与 make() 不同。您可能会得到类似这样的结果。

<?php

[
    'id'         => 1,
    'first'      => 'Rachel',
    'email'      => '[email protected]',
    'phone'      => '741-241-2356',
    'avatar'     => 'http://lorempixel.com/800/400/',
    'login'      => null,
    'created_at' => '2020-05-08 14:52:10',
    'updated_at' => '2020-05-08 14:52:10',
];

make() 类似,您可以提供一个计数来插入并返回一个对象数组。

<?php

$users = $fabricator->create(100);

最后,您可能需要使用完整的数据库对象进行测试,但实际上并没有使用数据库。 create() 接受第二个参数来允许模拟对象,返回具有额外数据库字段的对象,而无需实际访问数据库。

<?php

$user = $fabricator(null, true);

$this->assertIsNumeric($user->id);
$this->dontSeeInDatabase('user', ['id' => $user->id]);

指定测试数据

生成的数据很棒,但有时您可能希望为测试提供一个特定字段,而不会影响格式化程序的配置。与其为每个变体创建一个新的 Fabricator,不如使用 setOverrides() 来指定任何字段的值。

<?php

$fabricator->setOverrides(['first' => 'Bobby']);
$bobbyUser = $fabricator->make();

现在,使用 make()create() 生成的任何数据都将始终使用“Bobby”作为 first 字段。

<?php

[
    'first'  => 'Bobby',
    'email'  => '[email protected]',
    'phone'  => '251-806-2169',
    'avatar' => 'http://lorempixel.com/800/400/',
    'login'  => null,
];

[
    'first'  => 'Bobby',
    'email'  => '[email protected]',
    'phone'  => '525-214-2656 x23546',
    'avatar' => 'http://lorempixel.com/800/400/',
    'login'  => null,
];

setOverrides() 可以接受第二个参数来指示这是否应该是持久覆盖或仅用于单次操作。

<?php

$fabricator->setOverrides(['first' => 'Bobby'], $persist = false);
$bobbyUser = $fabricator->make();
$bobbyUser = $fabricator->make();

请注意,在第一次返回后,fabricator 停止使用覆盖。

<?php

[
    'first'  => 'Bobby',
    'email'  => '[email protected]',
    'phone'  => '741-857-1933 x1351',
    'avatar' => 'http://lorempixel.com/800/400/',
    'login'  => null,
];

[
    'first'  => 'Hans',
    'email'  => '[email protected]',
    'phone'  => '487-235-7006',
    'avatar' => 'http://lorempixel.com/800/400/',
    'login'  => null,
];

如果没有提供第二个参数,则传递的值将默认持久化。

测试助手

通常,您只需要一个用于测试的一次性假对象。测试助手提供了 fake($model, $overrides, $persist = true) 函数来完成此操作。

<?php

helper('test');
$user = fake('App\Models\UserModel', ['name' => 'Gerry']);

这等效于

<?php

use CodeIgniter\Test\Fabricator;

$fabricator = new Fabricator('App\Models\UserModel');
$fabricator->setOverrides(['name' => 'Gerry']);
$user = $fabricator->create();

如果您只需要一个假对象,而无需将其保存到数据库,则可以将 false 传递到持久化参数中。

表计数

通常,您的假数据将取决于其他假数据。 Fabricator 为每个表提供了您创建的假项目数量的静态计数。考虑以下示例。

您的项目有用户和组。在您的测试用例中,您希望创建具有不同大小的组的各种场景,因此您使用 Fabricator 创建了一堆组。现在您想创建假用户,但不想将它们分配给不存在的组 ID。您的模型的假方法可能如下所示。

<?php

namespace App\Models;

class UserModel
{
    protected $table = 'users';

    public function fake(Generator &$faker)
    {
        return [
            'first'    => $faker->firstName,
            'email'    => $faker->email,
            'group_id' => mt_rand(1, Fabricator::getCount('groups')),
        ];
    }
}

现在创建一个新用户将确保它属于一个有效的组: $user = fake(UserModel::class);

方法

Fabricator 在内部处理计数,但您也可以访问这些静态方法来帮助使用它们。

getCount(string $table): int

返回特定表的当前值(默认值:0)。

setCount(string $table, int $count): int

手动设置特定表格的值,例如,如果您创建了一些测试项目,但没有使用制造器,而您仍然希望将这些项目计入最终计数。

upCount(string $table): int

将特定表格的值增加一并返回新值。(这是在内部使用 Fabricator::create() 时所使用的)。

downCount(string $table): int

将特定表格的值减少一并返回新值,例如,如果您删除了一个假项目,但希望跟踪更改。

resetCounts()

重置所有计数。建议在测试用例之间调用此方法(尽管使用 CIUnitTestCase::$refresh = true 会自动执行此操作)。