leoloso/faker

Faker (fzaninotto/faker) 的 WordPress 版本 PoP 的分支,由 Brain Monkey 实现


README

这是一个用于 Brain Faker 的分支,用于在 PoP 中使用。

--

使用 FakerBrain 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 是一个具有 Brain Faker 功能的 Faker 实例,基本是一个方法 ->wp()
  • $wpFaker 是一个 \Brain\Faker\Providers 的实例,是 Brain Faker 提供的所有方法的入口点。

建议使用属性的 doc block,因为 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_postget_post_field
  • WP_User 实例,并模拟相关函数如 get_userdataget_user_byuser_can
  • WP_Term 实例,并模拟相关函数如 get_termget_term_by
  • WP_Comment 实例
  • WP_Site 实例,并模拟相关函数如 get_site
  • WP_Post_type 实例,并模拟相关函数如 get_post_type_objectpost_type_exists
  • WP_Taxonomy 实例,并模拟相关函数如 get_taxonomytaxonomy_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篇文章,所有文章类型为“页面”,状态为“发布”。

如果没有提供,所有属性都将随机生成。

除了$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的方法列表包括

对于posts

  • $this->wpFaker->posts()
  • $this->wpFaker->atLeast{$n}Posts()
  • $this->wpFaker->atMosts{$n}Posts()
  • $this->wpFaker->between{$min}and{$max}Posts()
  • $this->wpFaker->post()

对于users

  • $this->wpFaker->users()
  • $this->wpFaker->atLeast{$n}Users()
  • $this->wpFaker->atMosts{$n}Users()
  • $this->wpFaker->between{$min}and{$max}Users()
  • $this->wpFaker->user()

对于terms

  • $this->wpFaker->terms()
  • $this->wpFaker->atLeast{$n}terms()
  • $this->wpFaker->atMosts{$n}terms()
  • $this->wpFaker->between{$min}and{$max}Terms()
  • $this->wpFaker->term()

对于comments

  • $this->wpFaker->comments()
  • $this->wpFaker->atLeast{$n}comments()
  • $this->wpFaker->atMosts{$n}comments()
  • $this->wpFaker->between{$min}and{$max}Comments()
  • $this->wpFaker->comment()

对于sites

  • $this->wpFaker->sites()
  • $this->wpFaker->atLeast{$n}sites()
  • $this->wpFaker->atMosts{$n}sites()
  • $this->wpFaker->between{$min}and{$max}Sites()
  • $this->wpFaker->site()

对于post types

  • $this->wpFaker->postTypes()
  • $this->wpFaker->atLeast{$n}PostTypes()
  • $this->wpFaker->atMosts{$n}PostTypes()
  • $this->wpFaker->between{$min}and{$max}PostTypes()
  • $this->wpFaker->postType()

对于taxonomy

  • $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对象中不存在但在WordPress通过魔术方法__get提供的属性,在伪造对象上也是可用的,例如进行$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_namelast_nameuser_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()的实例的ID
  • wp_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() 函数,但仅在传递的 IDs 与生成的实例匹配时调用;

分类的模拟内容

在创建伪造的 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 支持一些提供程序的本地化

要获取 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 上作为 brain/faker 提供。

通过 Composer 它需要

  • fzaninotto/Faker (MIT)
  • brain/monkey (MIT)
  • mockery/mockery (BSD-3-Clause) - Brain Monkey 所需
  • antecedent/patchwork (MIT) - Brain Monkey 所需