【问题标题】:Symfony 2.8 : inject mocked repository in a test controllerSymfony 2.8:在测试控制器中注入模拟存储库
【发布时间】:2017-05-15 09:15:50
【问题描述】:

我尝试为自定义控制器创建测试。在这个中,这是执行的:

代码

$users = $this->getDoctrine()
                ->getRepository('MyBundle:User')
                ->findAllOrderedByName();

在测试控制器中,这就是我正在做的:

$entityManager = $this
            ->getMockBuilder('Doctrine\ORM\EntityManager')
            ->setMethods(['getRepository', 'clear'])
            ->disableOriginalConstructor()
            ->getMock();

        $entityManager
            ->expects($this->once())
            ->method('getRepository')
            ->with('MyBundle:User')
            ->will($this->returnValue($userRepositoryMock));


        // Set the client
        $client = static::createClient();
        $client->getContainer()->set('doctrine.orm.default_entity_manager', $entityManager);

问题

但最后测试失败了,因为我的模拟似乎没有被使用:

tests\MyBundle\Controller\ListingControllerTest::testAllAction 方法名称的期望失败等于字符串:getRepository 当被调用 1 次时。 方法预期调用1次,实际调用0次。

有什么想法吗?

编辑

以下关于 cmets 的评论,我创建了一个服务:

services:
    my.user.repository:
        class:   MyBundle\Entity\UserRepository
        factory: ['@doctrine.orm.default_entity_manager', getRepository]
        arguments:
          - MyBundle\Entity\User

所以现在,我“只是”必须模拟回购:

$userRepositoryMock = $this->getMockBuilder('MyBundle\Entity\UserRepository')
            ->disableOriginalConstructor()
            ->setMethods(['findAllOrderedByName'])
            ->getMock();

        $userRepositoryMock
            ->expects($this->once())
            ->method('findAllOrderedByName')
            ->will($this->returnValue($arrayOfUsers));

并将其注入容器中:

$client->getContainer()->set('my.user.repository', $userRepositoryMock);

但我还是有同样的问题

【问题讨论】:

  • 尝试用简单的doctrine替换服务名称doctrine.orm.default_entity_manager
  • 我有同样的结果

标签: symfony phpunit symfony-2.8


【解决方案1】:

不要在测试类中注入容器/实体管理器,而是注入理论存储库directly。另外,不要在测试中创建内核,测试应该运行得很快。

更新 1: 测试服务应该需要构造函数中的存储库。因此,在您的测试中,您可以将其替换为模拟。 $client = static::createClient() 用于使用夹具(功能测试)针对真实数据库测试控制器。不要使用它来测试具有模拟依赖项的服务(单元测试)。

更新 2:单元测试示例:

class UserServiceTest extends UnitTestCase
{
    public function test_that_findAllWrapper_calls_findAllOrderedByName()
    {

        //GIVEN
        $arrayOfUsers = [$user1, $user2];

        $userRepo = $this->getMockBuilder('AppBundle\Repository\UserRepository')
            ->disableOriginalConstructor()
            ->getMock();
        $userRepo
            ->expects($this->once())
            ->method('findAllOrderedByName')->willReturn($arrayOfUsers);


        $userService = new UserService($userRepo);

        //WHEN
        $result = $userService->findAllWrapper();

        //THEN
        $this->assertEquals($arrayOfUsers, $result);
    }
}

class UserService {
    private $userRepo;

    public function __construct(UserRepository $repo)
    {
        $this->userRepo = $repo;
    }

    public function findAllWrapper()
    {
        return $this->userRepo->findAllOrderedByName();
    }
}

【讨论】:

  • @enrico69 我添加了一些解释,你正在混合单元和功能测试
  • 好的,感谢您的解释,我没有意识到我在测试控制器时正在混合单元测试和功能测试。但是,你能给我举个例子吗:“测试服务应该在构造函数中需要存储库。所以在你的测试中你可以用模拟替换它”
  • 我认为关键是您根本不应该尝试对控制器操作进行单元测试。获取您尝试测试的功能并将其移动到它自己的服务中。然后测试服务。您的控制器代码最终应该会非常精简。
  • @cerad 我不是单元测试而是功能测试控制器,控制器已经很苗条了。
  • @enrico69 - 玩得开心。根据定义,您几乎不会嘲笑任何功能测试。如果你想测试你的 UserRepository::findAllOrderedByName 方法,那么你应该在不使用容器或任何东西的情况下对其进行单元测试。但这当然取决于你。
猜你喜欢
  • 1970-01-01
  • 2021-09-01
  • 1970-01-01
  • 2018-12-22
  • 2021-12-23
  • 1970-01-01
  • 2021-10-03
  • 1970-01-01
  • 2011-09-10
相关资源
最近更新 更多