er1z/fakemock

一个用于为注解类属性提供模拟功能的库

0.3.1 2019-01-19 22:24 UTC

This package is auto-updated.

Last update: 2024-09-20 11:09:45 UTC


README

Build Status Scrutinizer Code Quality Code Coverage

Faker 是一个用于模拟的神奇工具,但它有一个缺点——你需要做很多工作才能映射所有需要的东西。特别是当你与 DTOs/Entities 一起工作时,并且它们已经配置了一些断言——开发者必须从头开始创建非常规的规则。

这个库解决了这个问题。我引入了一个 FakeMock 库,它可以处理填充你需要的尽可能多的对象。

目录

安装

安装库

composer require er1z/fakemock

快速示例

我们假设所有自动加载器配置已设置,因此创建(或重用)您的 DTO

use Er1z\FakeMock\Annotations\FakeMock as FakeMock;
use Er1z\FakeMock\Annotations\FakeMockField as FakeMockField;

/**
 * @FakeMock()
 */
class MyDto {
    
    /**
     * @FakeMockField()
     */
    public $username;
    
}

现在——用一些随机数据填充上面的内容

$fakemock = new Er1z\FakeMock\FakeMock();

$dto = new MyDto();
$fakemock->fill($dto);

echo $dto->username;   // mr.handsome

发生了什么——这里使用了 name guesser,因此假设 $username 可能包含用户的登录名。但猜测并不总是适合你的需求。你可以指定任何 Faker 的方法来用随机数据填充它

/**
 * @FakeMockField("name")
 */
public $username;

最终生成一些随机的前名和姓氏。

配置

大部分行为都通过注解控制。我们可以指定两种类型的配置:全局(对象作用域)和局部(属性作用域)。全局作用域所有可用属性

局部作用域

局部作用域配置构造函数有从字符串参数创建注解的可能性,该参数填充到 faker 键。

填充多个对象

开发者很懒,我也是——你只需要关心你需要的事情。所以让我们填充一些对象

$fakemock = new FakeMock();

$results = [];

for( $a=0; $a<1000; $a++ )
{
    $results[] = $fakemock->fill(MyDto::class);
}

就是这样。它们都是新实例,所以不用担心任何引用。

有时需要条件性地填充对象。让我们尝试用填充每第三个生成的对象。首先,声明一个字段组

use Er1z\FakeMock\Annotations\FakeMock as FakeMock;
use Er1z\FakeMock\Annotations\FakeMockField as FakeMockField;

/**
 * @FakeMock()
 */
class GroupedDto {

    /**
     * @FakeMockField(groups={"first"})
     */
    public $field;
    
}

生成

$fakemock = new FakeMock();

$results = [];

for( $a=0; $a<1000; $a++ )
{
    $results[] = $fakemock->fill(MyDto::class, $a%3==0 ?? 'first');
}

现在,检查你的结果。这种行为类似于 Symfony 的 验证组

phpDoc

如果无法猜测,并且你没有映射任何特定的 Faker 类型,FakeMock 会尝试根据 phpDoc 变量类型猜测类型

use Er1z\FakeMock\Annotations\FakeMock as FakeMock;
use Er1z\FakeMock\Annotations\FakeMockField as FakeMockField;

/**
 * @FakeMock()
 */
class DocDTO {
    
    /**
     * @var float
     * @FakeMockField()
     */
    public $field;
    
}
$f = new FakeMock();
$obj = new DocDTO();

$data = $f->fill($obj);

var_dump($data->field); // eg. 1.24422

支持的 DTOs

FakeMock 依赖于 PropertyAccess 组件,因此支持不同类型的 DTOs,甚至是 Doctrine 实体。你可以保留对象公开的字段,也可以通过设置器和获取器封装数据

use Er1z\FakeMock\Annotations\FakeMock as FakeMock;
use Er1z\FakeMock\Annotations\FakeMockField as FakeMockField;

/**
 * @FakeMock()
 */
class EncapsulatedDTO
{
    /**
     * @FakeMockField();
     */
    protected $field;
    
    public function getField()
    {
        return $this->field;
    }
    
    public function setField($field)
    {
        $this->field = $field;
    }
    
}

这就可以正常工作。

断言

如果你的项目使用 Symfony 的验证 组件,可以利用验证规则来告诉 FakeMock 如何生成字段内容。例如

use Er1z\FakeMock\Annotations\FakeMock as FakeMock;
use Er1z\FakeMock\Annotations\FakeMockField as FakeMockField;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @FakeMock()
 */
class ValidatedDTO {
    
    /**
     * @FakeMockField()
     * @Assert\Email()
     */
    public $email;
    
}

调用 fill 方法对此对象将产生 $email 字段上的假电子邮件地址。

支持的断言

生成器

装饰器/条件

FakeMock 足够智能,可以猜测你想要什么——断言也针对指定的 phpDoc 类型进行了装饰,例如,如果你指定了 LessThan 约束和 @var float,你将得到浮点值,依此类推。这个功能在需要 DateTimeInterface 字符串时很有用

/**
 * @FakeMock()
 * @Assert\DateTime()
 * @var string
 */

内部架构

FakeMock 是一个拥有大量测试的库,所以如果你想要贡献并担心你的代码会搞乱某些东西,你不必担心。

模块化架构允许增强和扩展功能。主要入口点是一个 FakeMock 类,它需要三个元素

  • Metadata\FactoryInterface — 构建字段元数据的一些信息,例如是否需要处理,指定了哪些规则等等
  • GeneratorChainInterface — 维护一个字段数据生成器的列表
  • DecoratorChainInterface — 包含一个装饰器列表,可以根据各种规则修改生成的值,例如将 DateTimeInterface 转换为字符串

几乎所有的模块都可以通过传递接口或构造函数参数来覆盖。所以你可以自由地玩转所有组件。

逐步生成数据

  1. 创建一个指定要生成的对象/FQCN 的 FakeMock 实例(如果 FQCN,则静默实例化),
  2. 将对象传递给 Metadata\FactoryInterface 以获取主要对象配置
  3. 如果对象已配置,则遍历对象属性,创建 FieldMetadata,合并一些配置变量与对象配置,并检查是否指定了组以及是否需要处理
  4. 告诉 GeneratorChainInterface getValueForField。可用的适配器逐个执行,直到其中一个返回非空值
  5. 运行 DecoratorChainInterfacegetDecoratedValue —— 通常,它们都是逐个运行的,除非当前处理返回 false,这会断开链
  6. 通过访问器设置生成的值

默认生成器链

  1. TypedGenerator —— 处理两种情况:valueregex。不多也不少
  2. RecursiveGenerator —— 如果变量类在 phpDoc 中指定了 FQCN,它将被处理,除非将 recursive 字段标志设置为 false
  3. 如果已安装并可用 symfony/validator 包,则会对 AssertGenerator 进行检查
  4. FakerGenerator —— 提供生成指定 Faker 生成器的方法,或通过 NameGuesser 猜测字段内容
  5. PhpDocGenerator —— 根据属性类型生成数据
  6. LastResortGenerator —— 如果所有上述方法都失败,则以字符串形式生成简单的名称

默认装饰器链

  1. AssertDecorator —— 将值限制在验证规则中——其行为由 satisfyAssertsConditions 字段配置控制
  2. PhpDocDecorator —— 转换值类型

高级概念

这是在库内执行更复杂操作所需步骤的“骨架”。例如,我们想要在 DTO 上使用映射的接口/抽象。假设结构

interface SomeDTOInterface {
    
}
use Er1z\FakeMock\Annotations\FakeMock as FakeMock;
use Er1z\FakeMock\Annotations\FakeMockField as FakeMockField;

/**
 * @FakeMock()
 */
class InnerDTO implements SomeDTOInterface {
    
    /**
     * @FakeMockField()
     */
    public $field;
    
}
use Er1z\FakeMock\Annotations\FakeMock as FakeMock;
use Er1z\FakeMock\Annotations\FakeMockField as FakeMockField;

/**
 * @FakeMock()
 */
class MainDTO {
    
    /**
     * @FakeMockField()
     * @var SomeDTOInterface
     */
    public $nested;
    
}

运行基本的 FakeMock 场景将不会产生任何东西——$nestednull。我们必须告诉库将哪个对象映射到所需的接口。

$generators = GeneratorChain::getDefaultGeneratorsSet();

foreach($generators as $g)
{
    if( $g instanceof RecursiveGenerator::class )
    {
        $g->addClassMapping(SomeDTOInterface::class, InnerDTO::class);
    }
}

$generatorChain = new GeneratorChain($generators);

$fakemock = new FakeMock(null, $generatorChain);

$mainDto = new MainDto();
$result = $fakemock->fill($mainDto);

当然,你还可以通过全局和/或局部作用域上的注解来映射接口

use Er1z\FakeMock\Annotations\FakeMock as FakeMock;
use Er1z\FakeMock\Annotations\FakeMockField as FakeMockField;

/**
 * @FakeMock(classMappings={"Namespace\SomeDTOInterface"=>"Some\Other\Class"})
 */
class MainDTO {
    
    /**
     * @FakeMockField()
     * @var SomeDTOInterface
     */
    public $nested;
    
    /**
     * @FakeMockField("mapClass"="Some\Other\Class")
     * @var SomeDTOInterface
     */
    public $secondNested;
    
}

变更日志

0.3.1

  • 修复:值检索到基于 PropertyInfo 的方法

0.3

  • 新功能:指定 Faker 的区域设置和 Faker 生成器注册表

0.2.1

  • 修复:正确处理断言组

0.2

  • 新功能:递归字段处理

0.1

  • 第一个公共版本