brain/faker

通过 Brain Monkey 为 WordPress 提供 Faker (phpfaker/faker)

dev-master 2022-11-03 09:05 UTC

This package is auto-updated.

Last update: 2024-08-30 01:49:37 UTC


README

license travis-ci status codecov.io PHP version requirement

使用 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 是一个 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_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 方法的列表包括

对于 帖子

  • $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公开的属性,在伪造对象上也是可用的,例如执行$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() 函数,但仅当调用带有生成实例的 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 支持其一些提供者的本地化

要获取 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 需要