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)
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
Or just don't unit test your service but make functional tests with real database interactions
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With