为单元测试对象创建工厂的最佳方式是什么?

时间:2017-01-12 作者:philosophyguy

我正在努力学习TDD,正在努力为自定义对象创建工厂。例如,如果我有一个自定义用户类型,并且该类型的所有用户都必须具有特定的功能,那么使用WP\\u UnitTest工厂创建一个用户,然后在使用对象之前在每个测试中手动添加该功能是很麻烦的。因为我需要在各种测试文件中使用这些对象,所以在每个测试文件/类中都有一个工厂函数是多余的,手动实现工厂方法的所有四种变体(create、create\\u and\\u get、create\\u many和create\\u and\\u get\\u many)是一件痛苦的事情。

为这样的单元测试对象创建工厂的最佳方法是什么?

2 个回复
SO网友:gmazzap

如果我理解正确,那么您正在搜索的是一个很好的创建库stubsmocks.

如果您使用PHPUnit,它有一个特性。看见https://phpunit.de/manual/current/en/test-doubles.html#test-doubles

例如,假设您有这样一个类:

namespace MyApp;

class MyCustomUser {

   public function __construct(\\WP_User $user) {
     $this->user = $user;
   }

   public function hasCapability($capability) {
      return $this->user->can($capability);
   }
}
您可以创建一个包含用户工厂的特征,并利用PHPUnit的模拟功能:

namespace MyApp\\Tests;

trait CreateUserTrait {

   protected function createUser(array $capabilitites) {

      $stub = $this->createMock(MyCustomUser::class);

      $has_capability = function($capability) use ($capabilitites) {
         return in_array($capability, $capabilitites, true);
      };

      $stub->method(\'hasCapability\')->will($this->returnCallback($has_capability));
   }
}
此时,在测试类中,您可以使用该特性并利用工厂:

namespace MyApp\\Tests;

use PHPUnit\\Framework\\TestCase;

class UserMockTest extends TestCase
{
    use CreateUserTrait;

    public function testUserFactoryWorks()
    {
        $userMock = $this->createUser([\'create_post\']);

        $this->assertTrue($userMock->hasCapability(\'create_post\'));
    }
}
当然,当您需要测试其他使用模拟对象的对象时,模拟和存根很有用。你不会嘲笑SUT的。

使用mock的一个很好的副作用是,我们在测试中没有使用任何WordPress函数或类(即使原始用户对象使用WordPressWP_User 对象),因此测试可以在不加载WordPress的情况下运行,使其成为真正的单元测试:如果加载WordPress,它将成为集成测试,而不是单元测试。

许多人觉得PHP的模拟语法有点难以理解。如果你是其中之一,你可能想看看Mockery, 它有一个更简单的API。

根据您的需要,Faker 可能也会有很大帮助。

注:以上代码需要PHP 5.5,假设PHPUnit版本为5。*需要PHP 5.6+

SO网友:J.D.

我写了一个tutorial on creating your own PHPUnit factories. 在这种情况下,您只需要扩展现有的用户工厂,WP_UnitTest_Factory_For_User. 如果你看看the source of that class, 您将看到,您不需要实现一系列不同的方法,只需要一种:create_object(). 你提到的其他方法也会用到这一点。

以下是用户工厂中的方法:

function create_object( $args ) {
    return wp_insert_user( $args );
}
很简单吧?

因此,您只需修改它,即可创建该工厂的子类,如下所示:

class WP_UnitTest_Factory_For_User_With_Cap extends WP_UnitTest_Factory_For_User {

    function create_object( $args ) {
        $user_id = parent::create_object( $args );

        if ( $user_id ) {
            $user = new WP_User( $user_id );
            $user->add_cap( \'my_custom_cap\' );
        }

        return $user_id;
    }
}
然后你可以在你的setUp() 方法:

$this->factory->user_with_cap = new WP_UnitTest_Factory_For_User_With_Cap( $this->factory );
现在在测试中,您可以替换$this->factory->user 具有$this->factory->user_with_cap 在您需要创建具有该cap的用户的每个位置。

另一种方法是创建自己的自定义抽象测试用例类,该类是WP_UnitTestCase 并从中扩展所有测试用例。然后可以在该类上创建工厂方法。然而,只有当您的所有测试用例都可以从一个父级扩展时,这才有效,例如,如果您有一些Ajax测试,情况可能就不一样了。

相关推荐