【问题标题】:How to mock a method from the class you are testing with Prophecy?如何模拟您正在使用 Prophecy 测试的类中的方法?
【发布时间】:2015-03-19 19:37:53
【问题描述】:

我想第一次使用Prophecy ("phpspec/prophecy-phpunit") 为我的类创建单元测试。我想测试一个在同一个服务中调用另一个函数的函数,代码如下:

class UserManager
{
    private $em;
    private $passwordHelper;

    public function __construct(\Doctrine\ORM\EntityManager $em, \MainBundle\Helper\PasswordHelper $passwordHelper)
     {
         $this->em = $em;
         $this->passwordHelper = $passwordHelper;
     }

     public function getUserForLdapLogin($ldapUser)
     {
          $dbUser = $this
              ->em
              ->getRepository('MainBundle:User')
              ->findOneBy(array('username' => $ldapUser->getUsername()));

         return (!$dbUser) ?
              $this->createUserFromLdap($ldapUser) :
              $this->updateUserFromLdap($ldapUser, $dbUser);
     }

我遇到的第一个问题是我使用了findOneByUsername,据我所知,Prophecy 不允许您:模拟魔术方法(_call 代表 EntityRepository),模拟不存在的方法,模拟您正在测试的课程。如果这些都是真的,那我就有点麻烦了,这意味着如果不测试类中的其他函数,我就无法测试这个函数。

到目前为止,我的测试如下所示:

class UserManagerTest extends \Prophecy\PhpUnit\ProphecyTestCase
{

      public function testGetUserForLdapLoginWithNoUser()
      {
          $ldapUser = new LdapUser();
          $ldapUser->setUsername('username');

          $em = $this->prophesize('Doctrine\ORM\EntityManager');
          $passwordHelper = $this->prophesize('MainBundle\Helper\PasswordHelper');

          $repository = $this->prophesize('Doctrine\ORM\EntityRepository');
          $em->getRepository('MainBundle:User')->willReturn($repository);
          $repository->findOneBy(array('username' => 'username'))->willReturn(null);

          $em->getRepository('MainBundle:User')->shouldBeCalled();
          $repository->findOneBy(array('username' => 'username'))->shouldBeCalled();

          $service = $this->prophesize('MainBundle\Helper\UserManager')
            ->willBeConstructedWith(array($em->reveal(), $passwordHelper->reveal()));

          $service->reveal();
          $service->getUserForLdapLogin($ldapUser);
     }
}

当然,测试失败是因为$em 上的承诺和存储库没有实现。如果我实例化我正在测试的类,测试将失败,因为该函数随后在同一个类上调用 createUserFromLdap() 并且未经过测试。

有什么建议吗?

【问题讨论】:

  • 我从来没用过,所以我帮不上忙。但是,如果您仍然停留在几天内(即可以应用问题赏金时),请使用 @halfer 联系我,我会添加一个。好问题!
  • 到目前为止我发现的最佳答案是,您的班级可能不遵循 S.O.L.I.D 原则并且没有单一职责。所以在你尝试使用预言之前先把你的课程弄清楚。

标签: php symfony phpunit prophecy


【解决方案1】:

第一个问题:

不要使用魔法,魔法是邪恶的。 __call 可能会导致不可预知的行为。

“$em 上的承诺,以及存储库没有实现”:

不要让你的代码依赖于类而是接口。 然后模拟 Interface 而不是 Class ! 您应该模拟 ObjectManager 而不是 EntityManager。 (不要忘记更改参数的类型)

最后一点:

公开之前。

$service->createUserFromLdap()
   ->shouldBeCalled()
   ->willReturn(null);

【讨论】:

    【解决方案2】:

    您要实现的是部分模拟,Prophecy 不支持。在这里https://github.com/phpspec/prophecy/issues/101https://github.com/phpspec/prophecy/issues/61了解更多信息。

    TL;DR; 设计您的类时要牢记单一职责,因此您不必模拟其他功能。

    【讨论】:

      【解决方案3】:

      关于你无法模拟不存在的方法的问题,你可以使用

      http://docs.mockery.io/en/latest/

      而不是预言。嘲弄允许你这样做。严格来说,这确实打破了良好设计的一些规则,但另一方面,有时它只是非常有用。无论如何,就功能而言,嘲弄非常相似,而且它同样直观且易于使用 imo。但是,他们还没有发布稳定版本,所以如果您决定使用它,请注意这一点。

      在这里你可以找到两个库的一个很好的比较

      http://everzet.com/post/72910908762/conceptual-difference-between-mockery-and-prophecy

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-09-28
        • 1970-01-01
        • 2011-08-13
        • 2020-02-17
        • 2021-09-17
        • 2023-03-05
        • 1970-01-01
        相关资源
        最近更新 更多