baxtian / wp_faker
WordPress 的 Faker (phpfaker/faker) 通过 Brain Monkey 实现
Requires
- php: >=7.2
- brain/monkey: ^2.3.1
- fakerphp/faker: ^1.20
Requires (Dev)
- inpsyde/php-coding-standards: ^1.0
- phpunit/phpunit: ^9.5
- squizlabs/php_codesniffer: ^3.6
This package is not auto-updated.
Last update: 2024-09-27 21:42:25 UTC
README
使用 Faker 和 Brain Monkey 来提供简单的 WordPress 对象和相关功能的生成。
测试设置
参考 Brain Monkey WordPress 设置,我们可以通过 Brain Faker 功能扩展它。
class FakerTestCase extends \PHPUnit\Framework\TestCase
{
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
/**
* @var \Faker\Generator
*/
protected $faker;
/**
* @var \Brain\Faker\Providers
*/
protected $wpFaker;
protected function setUp(): void
{
parent::setUp();
\Brain\Monkey\setUp();
$this->faker = \Brain\faker();
$this->wpFaker = $this->faker->wp();
}
protected function tearDown(): void
{
\Brain\fakerReset();
\Brain\Monkey\tearDown();
parent::tearDown();
}
}
除了 Brain Monkey 的常规设置外,我们在测试用例实例中存储了两个属性
$faker
是一个 Faker 实例,它通过 Brain Faker 功能扩展,基本是一个->wp()
方法$wpFaker
是一个\Brain\Faker\Providers
实例,它是 Brain Faker 提供的所有方法的入口点。
建议在属性上使用文档块,因为 IDE 将能够为所有可以调用的 Faker 和 Brain Faker 方法提供自动完成功能。
对于 README 的其余部分,假设此测试设置,例如,将使用 $this->wpFaker
在各个地方,但只要通过 \Brain\faker()->wp()
获得一个 Faker\Providers
实例,并在每个测试的末尾调用 \Brain\fakerReset
,Brain Faker 将按预期工作。
值得注意的是,与 Brain Monkey 一样,Brain Faker 不需要 PHPUnit,这里使用它只是为了方便。
可以伪造的内容
所有将扩展上述基本测试用例类的测试用例类都可以使用 $this->wpFaker
来生成
WP_Post
实例,并模拟相关函数如get_post
和get_post_field
WP_User
实例,并模拟相关函数如get_userdata
、get_user_by
、user_can
等WP_Term
实例,并模拟相关函数如get_term
和get_term_by
WP_Comment
实例WP_Site
实例,并模拟相关函数如get_site
WP_Post_type
实例,并模拟相关函数如get_post_type_object
和post_type_exists
WP_Taxonomy
实例,并模拟相关函数如get_taxonomy
和taxonomy_exists
WP_Error
实例
Brain Faker 生成的实例不仅是“假的”,因为它们不是来自数据库,而且实际上它们是“模拟实例”,即它们是使用 Mockery 获得的。
快速示例
class MyPostCase extends FakerTestCase
{
public function testPostsCanBeFaked()
{
$posts = $this->wpFaker->posts(10);
static::assertIsArray($posts);
static::assertCount(10, $posts);
foreach ($posts as $post) {
static::assertInstanceOf(\WP_Post::class, $post);
static::assertGreaterThanOrEqual(1, $post->ID);
static::assertIsString($post->post_title));
static::assertIsString($post->post_content));
static::assertIsArray($post->to_array());
}
}
}
$this->wpFaker->posts()
,我们用来生成假 WP_Post
实例的方法,接受第一个参数为要生成的帖子数量。
第二个可选参数可以用来固定生成的帖子的一些属性,例如以下代码
$this->wpFaker->posts(10, ['type' => 'page', 'status' => 'publish']);
可以用来获取 10 个帖子,所有帖子类型为 "page" 且状态为 "publish"。
如果不提供,所有属性都随机生成。
除了 $this->wpFaker->posts()
,Brain Faker 还提供了 $this->wpFaker->post()
,它返回一个模拟的 WP_Post
实例,并且像 posts()
一样,也接受一个可选的参数,该参数是一个属性数组,用于分配给它。
之前已经提到,$this->wpFaker->posts()
接受的第一个参数是我们想要生成的对象数量。
然而,这个参数是可选的,如果没有提供,Brain Faker 将生成一个随机数量的对象(可能是零)。
值得注意的是,调用 $this->wpFaker->posts()
(不带参数)与调用 $this->wpFaker->posts
相同,这通常在 Faker 中发生。
有时可能希望生成最小或最大数量的对象,为此 Brain Faker 提供了以下形式的两个方法集
$this->wpFaker->atLeast{$n}Posts()
,例如$this->wpFaker->atLeast6Posts()
$this->wpFaker->atMost{$n}Posts()
,例如$this->wpFaker->atMost20Posts()
请注意,对于介于 1 和 5 之间的数字,可以将其拼写为文字,因此以下都是有效的方法
$this->wpFaker->atLeastOnePost()
$this->wpFaker->atMostTwoPost()
$this->wpFaker->atLeastThreePosts()
$this->wpFaker->atMostFourPosts()
$this->wpFaker->atLeastFivePosts()
同时请注意,当数字为 One
(或 1
)时,实体可以是单数(例如,Post
而不是 Posts
)。
要设置生成实例的最小和最大数量,Brain Faker 提供了一组形式为的方法
$this->wpFaker->between{$min}And{$max}Posts()
例如
$this->wpFaker->betweenOneAndThreePosts()
$this->wpFaker->between6And10Posts()
$this->wpFaker->between0AndFourPosts()
所有这些“动态命名”方法都接受一个可选参数,该参数是一个属性数组,用于分配给生成的帖子
$posts = $this->wpFaker->atLeastTwoPosts(['type' => 'post']);
$pages = $this->wpFaker->betweenThreeAnd10Posts(['type' => 'page']);
不仅仅是帖子
上面所说的所有关于帖子的话都适用于其他支持的对象,实际上 Brain Faker 方法列表包括
对于 帖子
$this->wpFaker->posts()
$this->wpFaker->atLeast{$n}Posts()
$this->wpFaker->atMosts{$n}Posts()
$this->wpFaker->between{$min}and{$max}Posts()
$this->wpFaker->post()
对于 用户
$this->wpFaker->users()
$this->wpFaker->atLeast{$n}Users()
$this->wpFaker->atMosts{$n}Users()
$this->wpFaker->between{$min}and{$max}Users()
$this->wpFaker->user()
对于 术语
$this->wpFaker->terms()
$this->wpFaker->atLeast{$n}terms()
$this->wpFaker->atMosts{$n}terms()
$this->wpFaker->between{$min}and{$max}Terms()
$this->wpFaker->term()
对于 评论
$this->wpFaker->comments()
$this->wpFaker->atLeast{$n}comments()
$this->wpFaker->atMosts{$n}comments()
$this->wpFaker->between{$min}and{$max}Comments()
$this->wpFaker->comment()
对于 站点
$this->wpFaker->sites()
$this->wpFaker->atLeast{$n}sites()
$this->wpFaker->atMosts{$n}sites()
$this->wpFaker->between{$min}and{$max}Sites()
$this->wpFaker->site()
对于 帖子类型
$this->wpFaker->postTypes()
$this->wpFaker->atLeast{$n}PostTypes()
$this->wpFaker->atMosts{$n}PostTypes()
$this->wpFaker->between{$min}and{$max}PostTypes()
$this->wpFaker->postType()
对于 分类法
$this->wpFaker->taxonomies()
$this->wpFaker->atLeast{$n}Taxonomies()
$this->wpFaker->atMosts{$n}Taxonomies()
$this->wpFaker->between{$min}and{$max}Taxonomies()
$this->wpFaker->taxonomy()
对于 WP_Error
$this->wpFaker->errors()
$this->wpFaker->atLeast{$n}Errors()
$this->wpFaker->atMosts{$n}Errors()
$this->wpFaker->between{$min}and{$max}Errors()
$this->wpFaker->error()
关于返回的实例
对于所有返回的对象,Brain Faker 设置了所有 真实 WordPress 对象应有的公共属性。
感谢 Faker,所有属性都将具有随机但“真实”的值(电子邮件将是电子邮件,URL 将是 URL,密码将是密码,等等)。
在WordPress对象中未声明为属性,但通过魔法方法__get
使WordPress可用的属性,也存在于虚构对象中,例如执行$this->wpFaker->user->user_login
是完全有效的,即使user_login
是真实的WP_User
实例通过魔法方法提供的属性。
已经提到,返回的实例是通过Mockery获取的模拟对象。
重要的是要说,那些实例上并非所有方法都被模拟。
例如,对于帖子,只有WP_Post::to_array()
方法被模拟,但其他方法如WP_Post::filter()
没有被模拟,但当然可以“手动”模拟。
例如,以下代码是有效的
$this->wpFaker->post
->shouldReceive('filter')
->once();
Brain Faker模拟的方法当然会因对象而异。
此外,Brain Faker还模拟了一些WordPress“相关函数”。
以下是Brain Faker为支持的每种对象模拟的完整方法列表和函数。
被模拟的内容
帖子被模拟的内容
在创建虚构的WP_Post
对象时,Brain Faker使用以下模拟创建
- 所有的
WP_Post
对象属性,包括WordPress通过__get
提供的属性,例如page_template
; WP_Post::to_array()
方法,其工作方式与WordPress版本完全相同;get_post
函数;get_post_field
函数;
例如
class MyPostCase extends FakerTestCase
{
public function testGetPostCanBeFaked()
{
$this->wpFaker->post(['id' => 42, 'title' => 'The Answer']);
static::assertSame('The Answer', get_post(42)->post_title);
static::assertFalse(get_post(1));
static::assertIsString(get_post_field('post_content', 42));
}
}
当用伪造帖子的ID调用时(我们因为通过提供的数组强制执行而知道它),get_post
返回我们生成的伪造帖子。
然而,当用另一个ID调用时,它返回false
,就像WordPress对于未找到的帖子所做的那样。
模拟版本的get_post
将能够识别测试中(或任何情况下在调用Brain\fakerReset()
之前)创建的所有帖子,并将它们正确解决,对于未创建的帖子返回false。
请注意,由get_post
返回的对象是一个与伪造对象完全可比较的对象,但不是同一个实例,即严格的相等比较(===
)将失败。
对于get_post_field
也是如此,Brain Faker模拟的所有其他函数也以类似的方式工作。
用户被模拟的内容
在创建虚构的WP_User
对象时,Brain Faker使用以下模拟创建
- 所有的
WP_User
对象属性,包括许多WordPress通过__get
提供的属性,例如first_name
、last_name
、user_login
等; WP_User::to_array()
方法;WP_User::exists()
方法;WP_User::get_site_id()
方法;WP_User::has_cap()
方法;get_user_data()
函数get_user_by()
函数user_can()
函数
此外,伪造的WP_User
实例是Brain Faker创建的唯一具有附加方法的虚构对象,该方法WordPress对象没有,即__monkeyMakeCurrent()
。
当调用时,__monkeyMakeCurrent()
还将模拟以下函数
get_current_user_id
,它将返回调用__monkeyMakeCurrent()
的实例的IDwp_get_current_user
,它将返回调用__monkeyMakeCurrent()
的实例current_user_can
,它将调用(模拟的)has_cap
方法调用__monkeyMakeCurrent()
的实例
这非常强大,因为只需要一行代码就可以模拟大量的WordPress代码而无需加载WordPress。
更重要的是,考虑到伪造的WP_User
实例的模拟方式与真实的WordPress对象非常相似(这是所有虚构对象都适用的),例如角色和能力将非常“真实”。
例如,考虑以下代码:
class MyUserCase extends FakerTestCase
{
public function testCurrentUserCanBeFaked()
{
$this->wpFaker->user(['role' => 'editor'])->__monkeyMakeCurrent();
static::assertTrue(current_user_can('edit_others_pages'));
}
}
对于术语,模拟了什么?
在创建假的 WP_Term
对象时,Brain Faker 创建具有以下属性的模拟:
- 所有 的
WP_Term
对象属性; WP_Term::to_array()
方法;get_term
函数,但 仅 当使用与生成的实例匹配的参数调用时;get_term_by
函数,但 仅 当使用与生成的实例匹配的参数调用时;
对于评论,模拟了什么?
在创建假的 WP_Comment
对象时,Brain Faker 创建具有以下属性的模拟:
- 所有 的
WP_Comment
对象属性; WP_Term::to_array()
方法;
对于站点,模拟了什么?
在创建假的 WP_Site
对象时,Brain Faker 创建具有以下属性的模拟:
- 所有 的
WP_Site
对象属性,包括 WordPress 通过__get
使可用的属性; WP_Site::to_array()
方法;get_site()
函数,但 仅 当使用生成实例的 ID 调用时;
对于分类法,模拟了什么?
在创建假的 WP_Taxonomy
对象时,Brain Faker 创建具有以下属性的模拟:
- 所有 的
WP_Taxonomy
对象属性; get_taxonomy()
函数taxonomy_exists()
函数
对于帖子类型,模拟了什么?
在创建假的 WP_Post_Type
对象时,Brain Faker 创建具有以下属性的模拟:
- 所有 的
WP_Post_Type
对象属性; get_post_type_object()
函数post_type_exists()
函数
对于错误,模拟了什么?
在创建假的 WP_Error
对象时,Brain Faker 创建具有以下属性的模拟:
- 所有 的
WP_Error
对象属性,但值为空; - 所有 的
WP_Error
公共方法;
伪造的 WP_Error
实例是 Brain Faker 创建的唯一属性不是“随机填充”的模拟对象,而是 完全 模拟的,因此可以用作 WordPress 对象。
想象一些类似于以下的 WordPress 代码:
function maybeAddError(\WP_Error $error): \WP_Error
{
if (is_error_there()) {
$error->add('code', 'A message', 'some data');
}
return $error;
}
使用 Brain Faker(和 Brain Monkey),我们可以这样测试它:
class MyErrorCase extends FakerTestCase
{
/**
* Test that error have expected message and data
* when `is_error_there` returns true
*/
public function testMaybeAddErrorWhenErrorIsThere()
{
\Brain\Monkey\Functions\when('is_error_there')->justReturn(true);
$error = maybeAddError($this->wpFaker->error);
static::assertTrue($error->has_errors());
static::assertSame('A message', $error->get_error_message());
static::assertSame('some data', $error->get_error_data());
}
/**
* Test that error is empty when `is_error_there` returns false
*/
public function testMaybeAddErrorWhenErrorIsNotThere()
{
\Brain\Monkey\Functions\when('is_error_there')->justReturn(false);
$error = maybeAddError($this->wpFaker->error);
static::assertFalse($error->has_errors());
static::assertSame([], $error->get_error_messages());
}
}
请注意,即使所有 \WP_Error
方法都进行了模拟,也无法阻止在测试中覆盖已模拟的方法(这对于 Brain Faker 生成的所有假对象都适用)。
高级主题
关于 ID 的唯一性
对于支持 ID 的对象(帖子、用户、术语、评论),除非在选项数组中指定,否则将随机生成一个 ID。
值得注意的是,这些随机生成的 ID 在每个测试中都是 唯一的(或者至少在调用 Brain\fakerReset
之后)。对于用户,除了 ID 之外,在随机生成时电子邮件和登录名也是唯一的。
例如,执行 $this->wpFaker->atLeast50Users()
时,Brain Faker 将创建至少 50 个模拟用户实例,每个实例具有不同的 ID、电子邮件和用户登录名。
此功能基于 Faker unique()
修饰符。
如 Faker 文档所述,唯一生成器可以被“重置”。要这样做,当使用 Brain Faker 时,可以调用 $this->wpFaker->__resetUnique()
。
请注意,手动调用 __resetUnique()
(它在调用 Brain\fakerReset
时自动调用)可能会与 Brain Monkey 模拟的函数冲突,因此除非确切知道自己在做什么,否则应避免这样做。
本地化 Faker
要获得 Faker 的本地化版本,当使用 Brain Faker 时,可以将所需的区域设置传递给 \Brain\faker()
函数。
例如
protected function setUp(): void
{
parent::setUp();
\Brain\Monkey\setUp();
$this->faker = \Brain\faker('fr_FR');
$this->wpFaker = $this->wpFaker->wp();
}
当然,可以为不同的区域设置拥有多个 Faker(和 Brain Faker)实例,也可以在 setUp
中不实例化它们,而是在单个测试中实例化。
即使在那种情况下,在测试结束时调用一次 \Brain\fakerReset()
也将是正确的。
覆盖 Brain Faker 方法和函数
可以轻松覆盖 Brain Faker 为生成的对象模拟的所有方法。
例如
class MyUserCase extends FakerTestCase
{
public function testUserCanBeMadeNotExistent()
{
$user = $this->wpFaker->user(['id' => 123]);
$user->shouldReceive('exist')->once()->andReturn(false);
static::assertFalse($user->exists());
}
}
在上面的示例中,生成的用户已经有一个模拟的 exist
方法,该方法会返回 true,因为 ID 大于零。
然而,我们能够覆盖方法期望,并使其返回 false。
这是返回实例实际上是模拟对象的一个巨大优势。
不幸的是,由于底层 Brain Monkey 的限制,这种方法不能用于 Brain Faker 模拟的 函数。
例如
class MyUserCase extends FakerTestCase
{
public function testBrainMonkeyFunctionsExpectIsIgnored()
{
$this->wpFaker->user(['id' => 123]);
Brain\Monkey\Functions\expect('get_userdata')
->with(123)
->andReturn(false);
static::assertSame(123, get_userdata(123)->ID);
}
}
上面的代码片段显示了如何忽略使用 Brain Monkey 对 get_userdata
的“手动”模拟,因此当调用它的 ID 时,get_userdata
仍然返回模拟的用户。
为了解决这个问题,Brain Faker 提供了一个方法 $this->wpFaker->__monkeyFunction()
,可以用来替换 Brain Faker 模拟的函数的期望。
上面的测试可以重写如下
class MyUserCase extends FakerTestCase
{
public function testGetUserDataCanBeOverridden()
{
$this->wpFaker->user(['id' => 123]);
$this->wpFaker->__monkeyFunction('get_userdata')
->with(123)
->andReturn(false);
static::assertFalse(get_userdata(123));
}
}
需要注意的唯一事情是,__monkeyFunction
必须在模拟对象创建后调用。
这看起来像是在使用 Brain Faker 时,应该记住它模拟的所有函数,因此应该使用 $this->wpFaker->__monkeyFunction()
而不是 Brain\Monkey\Functions\expect
来模拟它们。
这不是真的。
$this->wpFaker->__monkeyFunction()
总是可以用来替换 Brain\Monkey\Functions\expect
,事实上,当与未由 Brain Faker 模拟的函数一起使用时,它将作为 Brain\Monkey\Functions\expect
的别名,因此当在测试中模拟使用 Brain Faker 的函数时,直接使用 $this->wpFaker->__monkeyFunction()
可能是个好主意。
安装、要求、许可
Brain Faker 根据 MIT 许可发布。
需要 PHP 7.1+
可以通过 Composer 安装,在 packagist.org 上作为 baxtian/wp_faker
提供。
通过 Composer 需要
fzaninotto/Faker
(MIT)brain/monkey
(MIT)mockery/mockery
(BSD-3-Clause) - 由 Brain Monkey 需要antecedent/patchwork
(MIT) - 由 Brain Monkey 需要