Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Mock EntityManager in a serviceTest with Symfony?

Tags:

Sorry my question is very long.

How to mock entityManager in a service test extending KernelTestCase ?

Now, the explanation and my tests...

I am using Symfony3.2. My Application is standard. I have some Controller and I use WebTestCase to test them.

Generaly, my Controller verify parameters, call a service/a manager, handle some variables and push them to view and my test are pretty simple in test extending WebTestCase.

/**
 * Test New Game Action
 */
public function testFooAction(){
    //We mock the Service
    $fooService = $this
        ->getMockBuilder(GameService::class)
        ->disableOriginalConstructor()
        ->getMock();
    $fooService->expects(self::once())
        ->method('barMethod')
        ->willReturn($result);

    //We create the client
    $client = static::createClient();
    $container = $client->getContainer();

    //I put my mock here
    $container->set('app.game-service', $fooService);

    //I launch the request
    $client->request('GET', '/foo');
    //I handle the response
    $response = $client->getResponse();

    //I do some tests like this one
    self::assertEquals(200, $response->getStatusCode());

}

As you can see, I do not call EntityManger, because I use Services and these lines to put my Services's Mock

    //We create the client
    $client = static::createClient();
    $container = $client->getContainer();

    //I put my mock here
    $container->set('app.game-service', $fooService);

I have a problem to mock Entity Manager in my services. My Controller is tested but not My Service.

Here is the constructor initialized a simple protected property entityManager. The problem is this protected as you will see further...

   /**
     * FooService constructor.
     * @param EntityManager $entityManager
     */
    public function __construct(EntityManager $entityManager)
    {
           $this->entityManager = $entityManager;
    }

To Test my Service, here is my initial code :

<?php
class FooServiceTest extends KernelTestCase
{
    /**
     * Foo Service.
     *
     * @var FooService
     */
    private $fooService;

    /**
     * Prepares the environment before running each test.
     */
    protected function setUp()
    {
        parent::setUp();

        self::bootKernel();
        $this->fooService = static::$kernel->getContainer()
            ->get('app.foo-service') //HERE IS HOW I HANDLE MY SERVICE TO TEST IT
        ;

    }

testStart Works perfectly, there is some database interaction.

I know that I can rollback data during test. But I want to Mock entityManager to verify the call of commit method.

I try this to mock entity manager in my setUp :

    protected function setUp()
    {
        parent::setUp();

        $entityManager = $this
            ->getMockBuilder('Doctrine\ORM\EntityManager')
            ->disableOriginalConstructor()
            ->getMock();
        $entityManager
            ->expects(once())  // I WANT EXACTLY ONE CALL TO COMMIT METHOD
            ->method('commit')  
            ->willReturn(null);

        self::bootKernel();
        $container = static::$kernel->getContainer();
        $container->set('doctrine.orm.entity_manager', $entityManager); // THIS LINE DOES NOTHING <=======
        $this->gameService = static::$kernel->getContainer()
            ->get('app.game-service')
        ;

Thes code doesn't work. Mock is not in place. I still have the true entityManager. I think it is because the container is already closed. Do it is nut usefull to set

Like a Barbarian, I change the entityManager property to public

And I do that :

    protected function setUp()
    {
        parent::setUp();

        $entityManagerMock = $this
            ->getMockBuilder('Doctrine\ORM\EntityManager')
            ->disableOriginalConstructor()
            ->getMock();
        $entityManagerMock
            ->expects(once())  // I WANT EXACTLY ONE CALL TO commit METHOD
            ->method('commit')  
            ->willReturn(null);

        self::bootKernel();
        $this->gameService = static::$kernel->getContainer()
            ->get('app.game-service')
        ;
        $this->gameService->entityManager = entityManagerMock;

It works perfectly. Test can be run. But it is NOT a good practice to have an entityManager in a public property

My question is : How to mock entityManager in a service test ?

(Sorry, I am not fluent in english)

like image 594
Alexandre Tranchant Avatar asked Dec 03 '16 17:12

Alexandre Tranchant


1 Answers

First of all in your code

$container->set('doctrine.orm.entity_manager'); // THIS LINE DOES NOTHING <=======

I'm sure you missed second parameter :)

Another note: don't mock what you don't own. In future you will run composer update and EntityManager's commit will have some optional isReallyCommit parameter. Your code will be broken, but you will not notice it, because tests are green. I understand that it very unlikely, but anyway it's just example. I think the good practice here is to

  • Have some adapter with entity manager incapsulated
  • Mock that adapter in service unit tests
  • Test your adapter with functional tests against real database without mocking anything

Or just don't unit test your service but make functional tests with real database interactions

like image 131
Nikita U. Avatar answered Sep 22 '22 16:09

Nikita U.