er1z / fakemock
一个用于为注解类属性提供模拟功能的库
Requires
- php: ^7.1
- doctrine/annotations: ^1.6
- fzaninotto/faker: ^1.8
- icomefromthenet/reverse-regex: ^0.1.0
- phpdocumentor/reflection-docblock: ^4.3
- symfony/property-access: ^4.1
Requires (Dev)
- php-coveralls/php-coveralls: ^2.1
- phpunit/phpunit: ^7.4
Suggests
- symfony/intl: To get a correct country code for Country assert
- symfony/validator: To enhance field options autodetection
This package is auto-updated.
Last update: 2024-09-20 11:09:45 UTC
README
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
转换为字符串
几乎所有的模块都可以通过传递接口或构造函数参数来覆盖。所以你可以自由地玩转所有组件。
逐步生成数据
- 创建一个指定要生成的对象/FQCN 的
FakeMock
实例(如果 FQCN,则静默实例化), - 将对象传递给
Metadata\FactoryInterface
以获取主要对象配置 - 如果对象已配置,则遍历对象属性,创建
FieldMetadata
,合并一些配置变量与对象配置,并检查是否指定了组以及是否需要处理 - 告诉
GeneratorChainInterface
getValueForField
。可用的适配器逐个执行,直到其中一个返回非空值 - 运行
DecoratorChainInterface
的getDecoratedValue
—— 通常,它们都是逐个运行的,除非当前处理返回false
,这会断开链 - 通过访问器设置生成的值
默认生成器链
TypedGenerator
—— 处理两种情况:value
或regex
。不多也不少RecursiveGenerator
—— 如果变量类在 phpDoc 中指定了 FQCN,它将被处理,除非将recursive
字段标志设置为false
- 如果已安装并可用
symfony/validator
包,则会对AssertGenerator
进行检查 FakerGenerator
—— 提供生成指定 Faker 生成器的方法,或通过NameGuesser
猜测字段内容PhpDocGenerator
—— 根据属性类型生成数据LastResortGenerator
—— 如果所有上述方法都失败,则以字符串形式生成简单的名称
默认装饰器链
AssertDecorator
—— 将值限制在验证规则中——其行为由satisfyAssertsConditions
字段配置控制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 场景将不会产生任何东西——$nested
是 null
。我们必须告诉库将哪个对象映射到所需的接口。
$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
- 第一个公共版本