noj/fabrica

用于创建实体工厂的库,用于测试

1.1.2 2021-10-04 11:23 UTC

This package is auto-updated.

Last update: 2024-09-04 18:10:40 UTC


README

Travis (.com) Latest Stable Version PHP Version Support GitHub Total Downloads

Fabrica 处理用于测试的对象的定义和创建。

结合其 Doctrine 集成,Fabrica 允许您在不进行模拟的情况下全面测试 CRUD 逻辑。请参阅 解决的问题 页面以获取完整解释。

安装

composer require noj/fabrica --dev

使用

设置

首先在测试套件中的某个位置初始化 Fabrica。对于 PHPUnit,可以使用 引导 选项完成此操作

<?php
require 'vendor/autoload.php';
Fabrica::loadFactories([__DIR__ . '/factories']);

基本用法

假设您有一个 User 实体,创建一个新的工厂 factories/UserFactory.php

Fabrica::define(User::class, function () {
    return [
        'username' => 'user123',
        'firstName' => 'Test',
        'lastName' => 'User',
    ];
});

现在在您的测试中,您可以创建一个新的 User 实例

$user = Fabrica::create(User::class);

// Check properties have been set
assertEquals('user123', $user->username);
assertEquals('Test', $user->firstName);
assertEquals('User', $user->lastName);

设置器

如果实体的属性只能通过设置器访问,则可以使用 @ 语法来调用方法

Fabrica::define(User::class, function () {
    return [
        '@setUsername' => 'user123',
        '@setFirstName' => 'Test',
        '@setLastName' => 'User',
    ];
});

给定一个值是数组,您可以使用多次后缀(*)来指示您希望对每个项目调用该方法

'@addPermission*' => ['USER', 'ADMIN'],

覆盖

在创建实体时,您可以覆盖任何默认值

/// UserFactory.php
Fabrica::define(User::class, function () {
    return [
        'username' => 'user123',
        'firstName' => 'Test',
        'lastName' => 'User',
        '@setAge' => 47,
    ];
});

/// UserTest.php
$user = Fabrica::create(User::class, function () {
    return [
        'firstName' => 'Another',
        '@setAge' => 24,
    ];
});

assertEquals('user1223', $user->username);
assertEquals('Another', $user->firstName);
assertEquals('User', $user->lastName);
assertEquals(24, $user->getAge());

创建多个

您可以这样创建多个实体

$users = Fabrica::createMany(User::class, 3);

关系

您可以自动创建相关实体。例如,如果您有一个属于 UserComment 实体,则可以定义一个工厂

Fabrica::define(Comment::class, function () {
    return [
        'title' => 'A test comment',
        'body' => 'This is a test',
        'author' => Fabrica::create(User::class),
    ];
});

每次创建 Comment 时,它都将有一个相关的 User

$comment = Fabrica::create(Comment::class);

assertInstanceOf(User::class, $comment->user);
assertEquals('user123', $comment->user->username);

您还可以定义关系的反向。例如,您可以定义每个创建的 User 应该有一个相关的 Comment

Fabrica::define(User::class, function () {
    return [
        '@setUsername' => 'user123',
        '@setFirstName' => 'Test',
        '@setLastName' => 'User',
        '@addComment' => Fabrica::create(Comment::class)
    ];
});

您可以创建多个子关系

Fabrica::define(User::class, function () {
    return [
        'comments' => Fabrica::createMany(Comment::class, 3),

        // or if you have a setter method, use the `*` suffix to call the method
        // once for each element of the array
        '@addComment*' => Fabrica::createMany(Comment::class, 3),
    ];
});

将创建一个具有 3 个 CommentsUser

如果实体有一个依赖于关系的属性,则可以这样定义

Fabrica::define(Comment::class, function () {
    return [
        'user' => Fabrica::create(User::class),
        'userFirstName' => Fabrica::property('user.firstName'),
    ];
});

覆盖关系属性

您还可以在创建实体时覆盖嵌套关系的属性

$comment = Fabrica::create(Comment::class, function () {
    return [
        'author.firstName' => 'John'
    ];
});

assertEquals('user123', $comment->user->username);
assertEquals('John', $comment->user->firstName);

对于一对一关系中的一个实体

$user = Fabrica::create(User::class, function () {
    return [
        'comments.1.title' => 'Only the 2nd comment has this title'
    ];
});

assertEquals('A test comment', $user->comments[0]->title);
assertEquals('Only the 2nd comment has this title', $user->comments[1]->title);

甚至每个实体

$user = Fabrica::create(User::class, function () {
    return [
        'comments.*.title' => 'Each comment now has this title'
    ];
});

foreach ($user->comments as $comment) {
    assertEquals('Each comment now has this title', $comment->title);
}

实体类型

而不是在创建实体时始终传递覆盖,您可以定义不同类型的实体

Fabrica::define(User::class, function () {
    return [
        'username' => 'bannedUser',
        'firstName' => 'Test',
        'lastName' => 'User',
    ];
})->type('banned');

$normalUser = Fabrica::create(User::class);
$bannedUser = Fabrica::create(User::class, 'banned');
$bannedUser2 = Fabrica::create(User::class, 'banned', function () {
    return ['firstName' => 'banned'];
});

扩展

如果子类型与父类型共享属性,则可以指定工厂从它扩展

Fabrica::define(User::class, function () {
    return [
        'username' => 'bannedUser'
    ];
})->type('banned')->extends(User::class);

$bannedUser = Fabrica::create(User::class, 'banned');
assertEquals('bannedUser', $bannedUser->username);
assertEquals('Test', $bannedUser->firstName);
assertEquals('User', $bannedUser->lastName);

您还可以从子类型扩展

Fabrica::define(User::class, function () {
    return [
        'permanent' => true
    ];
})->type('permaBanned')->extends(User::class, 'banned);

Doctrine 集成

Fabrica 随附一个 Doctrine 适配器,它将在创建时自动持久化您的实体。只需在引导文件中配置存储即可

Fabrica::setStore(new DoctrineStore($entityManager));

EntityManager 的来源以及其配置可能取决于您的应用程序。

Fabrica 提供了一种简单的方法,使用内存中的 SQLite 创建基于注解的 EntityManager,如果这符合您的需求

$entityManager = \Noj\Fabrica\Adapter\Doctrine\EntityManagerFactory::createSQLiteInMemory([__DIR__ . '/path/to/entities']);
Fabrica::setStore(new DoctrineStore($entityManager));

在测试之间刷新数据库

很可能是您希望在每次测试运行之前重置数据库的状态。有两种方法可以做到这一点

  • 如果您正在使用 PHPUnit 7.5 或更高版本,则可以在 phpunit.xml 中添加以下内容,这将在每个测试之间重置您的数据库
    <extensions>
        <extension class="Noj\Fabrica\Adapter\Doctrine\PHPUnit\RefreshDatabase" />
    </extensions>
  • 如果您正在使用 PHPUnit 的较低版本或只想为特定测试创建数据库,则可以将特质添加到您的测试类中
    class MyTest extends TestCase
    {
        use \Noj\Fabrica\Adapter\Doctrine\PHPUnit\NeedsDatabase;
    }

PHPUnit 断言

Fabrica 默认提供了一组 PHPUnit 断言,用于在测试过程中验证数据库的状态。

class MyTest extends TestCase
{
    use \Noj\Fabrica\Adapter\Doctrine\PHPUnit\DatabaseAssertions;
}

这提供了以下断言:

  • assertDatabaseContainsEntity(string $class, array $criteria = [])
  • assertDatabaseContainsEntities(string $class, int $amount, array $criteria = [])
  • assertDatabaseContainsExactlyOneEntity(string $class, array $criteria = [])
  • assertDatabaseDoesNotContainEntity(string $class, array $criteria = [])

注意:如果您正在使用上述描述的 NeedsDatabase,则断言已包含在内。

示例用法

public function test_it_creates_a_user()
{
    (new UserCreator)->create('test');
    self::assertDatabaseContainsEntity(User::class, ['username' => 'test'])'
}

Faker 集成

Fabrica 可以配置一个 faker 实例,以帮助在实体定义中生成假数据。

$faker = \Faker\Factory::create();
Fabrica::addDefineArgument($faker);

然后您将在定义回调中收到 faker 实例

use Faker\Generator as Faker;

Fabrica::define(User::class, function (Faker $faker) {
    return [
        'firstName' => $faker->firstName,
        'lastName' => $faker->lastName,
        'email' => $faker->email,
    ];
});

灵感