zizaco/factory-muff

此包已被废弃且不再维护。作者建议使用 league/factory-muffin 包代替。

此包的目标是能够快速创建对象,以便进行测试。

v3.3.0 2020-12-13 18:38 UTC

README

StyleCI Status Build Status Software License Latest Version Total Downloads

此包的目标是能够快速创建对象,以便进行测试。

它基本上是一个 "factory_girl",为PHP简化使用。

安装

PHP 5.4+ 和 Composer 是必需的。

在您的 composer.json 中,只需将 "league/factory-muffin": "^3.3" 添加到您的 "require-dev" 部分

{
    "require-dev": {
        "league/factory-muffin": "^3.3"
    }
}

Faker 支持由 Factory Muffin Faker 提供。如果您想启用 Faker 支持,则需要添加 "league/factory-muffin-faker": "^2.3" 也是

{
    "require-dev": {
        "league/factory-muffin": "^3.3",
        "league/factory-muffin-faker": "^2.3"
    }
}

升级

现有用户查看 升级指南 可能会有所帮助。

使用

介绍

这是 Factory Muffin 3.0 的使用指南。在本指南中,您将看到 "the xyz 函数可以调用"。您应该假设这些函数应该在 League\FactoryMuffin\FactoryMuffin 的实例上调用;您应该自己跟踪此实例,当然,您可以为最大灵活性拥有多个此类的实例。为了简单起见,我们的大多数示例都包括一个 $fm 变量。当使用 loadFactories 函数引入文件时,此变量实际上会可用。

模型定义

您可以使用 define 函数定义模型生成器。您可以这样调用它:$fm->define('Fully\Qualifed\ModelName')->addDefinitions('foo', 'bar'),其中 foo 是您希望在模型上设置的属性名称,而 bar 描述了您希望如何生成该属性。您也可以一次性定义多个属性,如下所示:$fm->define('Fully\Qualifed\ModelName')->setDefinitions('foo', 'bar')。请注意,这两个函数都追加到内部属性定义数组,而不是替换它。有关更多信息,请参阅生成器部分。

您还可以为您的模型定义多个不同的模型定义。您可以通过在模型类名称前加上您的 "group" 并跟一个冒号来实现这一点。这会导致您以这种方式定义模型:$fm->define('myGroup:Fully\Qualifed\ModelName')->addDefinitions('bar', 'baz')。您不需要在这里完全定义模型,因为我们首先会寻找不带 group 前缀的定义,然后应用您的 group 定义,在需要时覆盖属性定义。请注意,如果您想使用 group 前缀,必须 还创建一个不带 group 前缀的定义。

我们为您提供了在测试中实现此功能的一种巧妙方法。PHPUnit 提供了 setupBeforeClass 函数。在该函数内部,您可以调用 $fm->loadFactories(__DIR__ . '/factories');,这将包含工厂文件夹中的所有文件。在这些 PHP 文件中,您可以放置您的定义(调用 define 函数的所有代码)。

loadFactories 函数将遍历您指定的路径下的所有子文件夹以查找工厂文件,除了隐藏文件夹(即以 . 开头的文件夹)将被忽略。如果找不到您指定的工厂目录,它将抛出 League\FactoryMuffin\Exceptions\DirectoryNotFoundException 异常。

创建/实例化回调

您可以使用 setCallback 函数指定在模型创建/实例化时执行的回调,例如:$fm->define('MyModel')->setCallback(function ($object, $saved) {})。我们将把您的模型实例作为回调的第一个参数传递,并将布尔值作为第二个参数。布尔值将为 true 如果模型正被持久化到数据库(使用了 create 函数),否则为 false(使用了 instance 函数)。我们在底层使用的是 isPendingOrSaved 函数。请注意,如果您指定了回调并使用了 create 函数,我们将在执行回调前后尝试将您的模型保存到数据库。

生成器

可调用

如果需要更定制的解决方案,可以使用可调用生成器。您从闭包返回的任何内容,或有效的可调用内容,都将被设置为属性。闭包/可调用将以与上述创建/实例化回调相同的参数调用:您的模型实例作为第一个参数(以便您有更大的灵活性来按需修改它)和一个表示模型是否正被持久化到数据库的布尔值。在下面的示例中,我们将介绍如何使用闭包或可调用,以及如何使用 faker 生成属性。

示例 1

如您从本例中看到的,使用闭包生成属性的能力非常有用且灵活。这里我们使用它根据最初随机生成的 5 个词长的标题生成一个 slug。

$fm->define('MyModel')->setDefinitions([
    'title' => Faker::sentence(5),
    'slug' => function ($object, $saved) {
        $slug = preg_replace("/[^a-zA-Z0-9\/_|+ -]/", '', $object->title);
        $slug = strtolower(trim($slug, '-'));
        $slug = preg_replace("/[\/_|+ -]+/", '-', $slug);

        return $slug;
    },
]);
示例 2

这将把 foo 属性设置为调用 MyModel::exampleMethod($object, $saved) 返回的内容。

$fm->define('MyModel')->setDefinitions([
    'foo' => 'MyModel::exampleMethod',
]);
示例 3

这是一个使用我们伪造包装器设置几个不同属性的简单示例。

use League\FactoryMuffin\Faker\Facade as Faker;

$fm->define('MyModel')->setDefinitions([
    'foo'    => Faker::word(),          // Set the foo attribute to a random word
    'name'   => Faker::firstNameMale(), // Set the name attribute to a random male first name
    'email'  => Faker::email(),         // Set the email attribute to a random email address
    'body'   => Faker::text(),          // Set the body attribute to a random string of text
    'slogan' => Faker::sentence(),      // Set the slogan attribute to a random sentence
]);
示例 4

这将把 age 属性设置为介于 20 和 40 之间的随机数。

use League\FactoryMuffin\Faker\Facade as Faker;

$fm->define('MyModel')->setDefinitions([
    'age' => Faker::numberBetween(20, 40),
]);
示例 5

这将把 name 属性设置为随机的女性名字。因为我们首先调用了 unique 方法,所以属性应该在所有生成的模型之间是唯一的。如果您正在生成很多模型,请小心使用此方法,因为我们可能会用尽唯一的项。另外,请注意,调用 Faker::setLocale('whatever') 将重置内部的唯一列表。

use League\FactoryMuffin\Faker\Facade as Faker;

$fm->define('MyModel')->addDefinition('name', Faker::unique()->firstNameFemale());
示例 6

这将设置 profile_pic 属性为随机图像 URL,尺寸为 400x400。因为我们首先调用了 optional 方法,所以并非所有生成的模型都会设置图像 URL;有时我们会返回 null。

use League\FactoryMuffin\Faker\Facade as Faker;

$fm->define('MyModel')->addDefinition('profile_pic', Faker::optional()->imageUrl(400, 400));
更多

查看faker 库本身以了解所有可用方法。这里要涵盖的方法太多,它们自己的文档中也是如此。注意,如果您想调整底层 faker 实例,可以通过我们 faker 类的公共方法来实现。

工厂

工厂生成器可以用来设置模型之间的关系。工厂生成器将返回您要求其生成的模型的模型 ID。

示例 1

当我们创建一个 Foo 对象时,我们会发现 Bar 对象也会被生成并保存,并且它的 ID 将被分配给 Foo 模型的 bar_id 属性。

$fm->define('Foo')->addDefinition('bar_id', 'factory|Bar');

$fm->define('Bar')->addDefinition('baz', Faker::date('Y-m-d'));

创建和播种

create 函数将创建并保存您的模型,并且还会保存您使用 Factory 生成器生成的任何内容。如果您想创建多个实例,请查看 seed 函数,该函数在开始时接受一个额外的参数,即要生成的模型数量。该 seed 函数将重复调用 create 函数。

例如,让我们创建一个用户模型,并将其与多个位置和电子邮件模型关联。每个电子邮件也将有多个标记模型。

$user = $fm->create('User');
$profiles = $fm->seed(5, 'Location', ['user_id' => $user->id]);
$emails = $fm->seed(100, 'Email', ['user_id' => $user->id]);

foreach ($emails as $email) {
    $tokens = $fm->seed(50, 'Token', ['email_id' => $email->id]);
}

您可能会遇到以下异常

  • 如果定义的模型类找不到,将抛出 League\FactoryMuffin\Exceptions\ModelNotFoundException
  • 如果您尝试创建一个模型但尚未为其定义模型定义,将抛出 League\FactoryMuffin\Exceptions\DefinitionNotFoundException
  • 如果您的模型上的保存函数返回 false,将抛出 League\FactoryMuffin\Exceptions\SaveFailedException
  • 如果您的模型上的保存函数不存在,将抛出 League\FactoryMuffin\Exceptions\SaveMethodNotFoundException
  • 在尝试创建或保存模型时,您的模型可能抛出的任何其他异常。

还有 5 个其他辅助函数可用

  • 您可以使用 pending 返回所有等待保存的对象数组。
  • 您可以使用 isPending 与一个模型实例一起调用,以检查它是否会保存。
  • 您可以使用 saved 返回所有已保存的对象数组。
  • 您可以使用 isSaved 与一个模型实例一起调用,以检查它是否已保存。
  • 您可以使用 isPendingOrSaved 与一个模型实例一起调用,以检查它是否会保存或已保存。

另外,如果您不想使用数据库持久性,instance 函数仍然可用。

删除

您可以使用 deleteSaved 函数删除所有已保存的模型。请注意,您的已保存模型将按保存的顺序删除,以确保关系行为正确。

如果无法删除一个或多个模型,在我们尝试删除所有已保存模型之后,将引发 League\FactoryMuffin\Exceptions\DeletingFailedException。您可以通过 getExceptions 函数访问每个底层异常,该函数将返回一个异常数组。您可能会遇到以下异常

  • 如果您的模型上的删除函数返回false,将抛出 League\FactoryMuffin\Exceptions\DeleteFailedException
  • 如果您的模型上不存在删除函数,将抛出 League\FactoryMuffin\Exceptions\DeleteMethodNotFoundException
  • 在尝试删除模型时,您的模型抛出的任何其他异常。

建议您从PHPUnit的tearDownAfterClass函数中调用deleteSaved函数,但是,如果您正在使用Laravel的TestCase编写测试,您应该在调用parent::tearDown之前从tearDown方法中调用deleteSaved函数。此方法会刷新应用程序实例的绑定,Factory Muffin将无法执行其删除操作。此外,这将解除分配的异常处理程序,您将无法通过绑定解析异常来调试测试,从而模糊了真正的异常。

实际示例

首先,我们需要创建一些定义

# tests/factories/all.php

use League\FactoryMuffin\Faker\Facade as Faker;

$fm->define('Message')->setDefinitions([
    'user_id'      => 'factory|User',
    'subject'      => Faker::sentence(),
    'message'      => Faker::text(),
    'phone_number' => Faker::randomNumber(8),
    'created'      => Faker::date('Ymd h:s'),
    'slug'         => 'Message::makeSlug',
])->setCallback(function ($object, $saved) {
    // we're taking advantage of the callback functionality here
    $object->message .= '!';
});

$fm->define('User')->setDefinitions([
    'username' => Faker::firstNameMale(),
    'email'    => Faker::email(),
    'avatar'   => Faker::imageUrl(400, 600),
    'greeting' => 'RandomGreeting::get',
    'four'     => function() {
        return 2 + 2;
    },
]);

然后您可以在测试中使用这些工厂

# tests/TestUserModel.php

use League\FactoryMuffin\Faker\Facade as Faker;

class TestUserModel extends PHPUnit_Framework_TestCase
{
    protected static $fm;

    public static function setupBeforeClass()
    {
        // create a new factory muffin instance
        static::$fm = new FactoryMuffin();

        // you can customize the save/delete methods
        // new FactoryMuffin(new ModelStore('save', 'delete'));

        // load your model definitions
        static::$fm->loadFactories(__DIR__.'/factories');

        // you can optionally set the faker locale
        Faker::setLocale('en_EN');
    }

    public function testSampleFactory()
    {
        $message = static::$fm->create('Message');
        $this->assertInstanceOf('Message', $message);
        $this->assertInstanceOf('User', $message->user);
    }

    public static function tearDownAfterClass()
    {
        static::$fm->deleteSaved();
    }
}

更多信息

如果您想了解更多信息,以下资源可供您参考

集成

贡献

请查看我们的贡献指南以获取详细信息。

鸣谢

Factory Muffin基于Zizaco Zizuini在“Factory Muff”上的原始工作,并由Graham Campbell维护。在3.0版本发布之前,Scott Robertson也是共同维护者。感谢我们所有优秀的贡献者

安全

如果您在此软件包中发现安全漏洞,请向Graham Campbell发送电子邮件至[email protected]。所有安全漏洞都将得到及时处理。您可以在此查看我们的完整安全政策。

许可

Factory Muffin采用MIT许可证(MIT)

企业版

作为Tidelift订阅的一部分提供

《league/factory-muffin》及其他数千个包的维护者正在与Tidelift合作,为构建应用程序所使用的开源依赖项提供商业支持和维护。节省时间、降低风险,同时为使用的确切的依赖项支付维护者的费用。了解更多信息。点击这里