问题
I am hoping someone can shed some light on this issue I am facing.
[PROBLEM]
I have mocked out doctrine.orm.default_entity_manager service in my functional unit test. I inject this into the client service container so that I do not have to hit my DB during the course of my functional test. For my test that just involve a GET request I am able to verify that the controller I am testing is using my mocked service.
However, if I attempt to do a POST request by using the crawler with a form submission my mocked service does not persist. After the initial GET request the client seems to just inject doctrine.orm.default_entity_manager service again as it needs it and not my mocked version that I set in the clients service container.
In summary, during the GET request my mocked service is being used, but during the POST request EntityManager5144076565ee8_546a8d27f194334ee012bfe64f629947b07e4919__CG__\Doctrine\ORM\EntityManager is being used. [SEE CODE SNIPPET BELOW]
[QUESTION]
Is it possible to do what I am asking? I would like to have ALL my requests use the mocked service I defined. I want to have a functional test but avoid writing or reading from the database.
[SAMPLE CODE]
// Mocks
$entityRepository = $this
->getMockBuilder('Doctrine\ORM\EntityRepository')
->setMethods(array('findby'))->disableOriginalConstructor()
->getMock();
$entityRepository->expects($this->any())->method('findBy')
->will($this->returnValue(array()));
$em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
->setMethods(
array('getRepository', 'getClassMetadata', 'flush',
'persist'))->disableOriginalConstructor()
->getMock();
$em->expects($this->any())->method('flush')
->will($this->returnValue(FALSE));
$em->expects($this->any())->method('persist')
->will($this->returnValue(FALSE));
$em->expects($this->any())->method('getRepository')
->will($this->returnValue($entityRepository));
$em->expects($this->any())->method('getClassMetadata')
->will($this->returnValue(new ClassMetadata("test")));
// Create test client.
$client = static::createClient();
// Inject entity mock into service container.
$client->getContainer()
->set('doctrine.orm.default_entity_manager', $em, 'container');
// Define request
$crawler = $client->request('GET', '/locations/types/add');
// Verify a few things
$form = $crawler->selectButton('submit')->form();
$form['location_type[title]'] = "TEST TITLE";
$form['location_type[description]'] = "TEST DESCP";
$crawler = $client->submit($form);
回答1:
The issue here is that the kernel is booted after (during) every request:
protected function doRequest($request)
{
// avoid shutting down the Kernel if no request has been performed yet
// WebTestCase::createClient() boots the Kernel but do not handle a request
if ($this->hasPerformedRequest) {
$this->kernel->shutdown();
} else {
$this->hasPerformedRequest = true;
}
if ($this->profiler) {
$this->profiler = false;
$this->kernel->boot();
$this->kernel->getContainer()->get('profiler')->enable();
}
https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Client.php
So you need to replace doctrine with your mock after each request:
// Inject entity mock into service container.
$client->getContainer()
->set('doctrine.orm.default_entity_manager', $em, 'container');
// Define request
$crawler = $client->request('GET', '/locations/types/add');
// Inject entity mock into service container.
$client->getContainer()
->set('doctrine.orm.default_entity_manager', $em, 'container');
An easier way to use your mock globally would be to override the doctrine settings in config_test.yml
orm:
default_entity_manager: Acme/MyBundle/Test/MockDoctrineEM
http://symfony.com/doc/master/reference/configuration/doctrine.html
来源:https://stackoverflow.com/questions/15606191/functional-test-mock-service-does-not-persist-in-service-container