Search code examples
symfonyphpunitsymfony-2.8

Symfony 2.8 : inject mocked repository in a test controller


I try to create a test for a custom controller. In this one, this is what is executed:

Code

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

In the test controller, this is what I am doing:

$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);

Problem

But at the end the test fails because my mock seems to no be used:

tests\MyBundle\Controller\ListingControllerTest::testAllAction Expectation failed for method name is equal to string:getRepository when invoked 1 time(s). Method was expected to be called 1 times, actually called 0 times.

Any idea ?

EDIT

Following remarks on comments, I created a service:

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

So now, I "just" have to mock the repo:

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

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

and inject it into the container:

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

But I still have the same issue


Solution

  • Don't inject container/entity manager in the testing class, instead inject doctrine repositories directly. Also, don't create kernel in your test, tests should run fast.

    UPDATE 1: The testing service should require repository in constructor. So in your test you are able to replace it with mock. $client = static::createClient() is for testing controllers against the real database with fixtures (functional test). Don't use it for testing services with mocked dependencies (unit test).

    UPDATE 2: Example of unit test:

    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();
        }
    }