I have created a very simple REST controller in Symony2 with Database insert/updates/deletes in the controller actions.
Is there a nice way to write unit/integration tests for these controller actions without polluting the production database? Do I have to work with different environments - or is there a proposed way from the framework vendor for this?
Current Controller Example:
public function postAction()
{
$json = $this->getRequest()->getContent();
$params = json_decode($json);
$name = $params->name;
$description = $params->description;
$sandbox = new Sandbox();
$sandbox->setName($name);
$sandbox->setDescription($description);
$em = $this->getDoctrine()->getManager();
$em->persist($sandbox);
$em->flush();
$response = new Response('/sandbox/'.$sandbox->getId());
$response->setStatusCode(201);
return $response;
}
Current Test Example:
class SandboxControllerTest extends WebTestCase
{
public function testRest()
{
$client = static::createClient();
$crawler = $client->request('POST', '/service/sandbox', array(), array(), array(), json_encode(array('name' => 'TestMe', 'description' => 'TestDesc')));
$this->assertEquals(
201, $client->getResponse()->getStatusCode()
);
}
}
In my opinion you should definitely avoid change database with your tests.
My favourite way to achieve this is inject entity manager mock inside a test client. For example:
public function testRest()
{
// create entity manager mock
$entityManagerMock = $this->getMockBuilder('Doctrine\ORM\EntityManager')
->setMethods(array('persist', 'flush'))
->disableOriginalConstructor()
->getMock();
// now you can get some assertions if you want, eg.:
$entityManagerMock->expects($this->once())
->method('flush');
// next you need inject your mocked em into client's service container
$client = static::createClient();
$client->getContainer()->set('doctrine.orm.default_entity_manager', $entityManagerMock);
// then you just do testing as usual
$crawler = $client->request('POST', '/service/sandbox', array(), array(), array(), json_encode(array('name' => 'TestMe', 'description' => 'TestDesc')));
$this->assertEquals(
201, $client->getResponse()->getStatusCode()
);
}
One thing with this solution which you should be aware is that you need inject your mocked service before each request. This is because the client reboots a kernel between each request (which means that the container is rebuild as well).
edit:
My GET approach in controller's tests is that I can mock entity repositories and so on in order to stub every getting data from db but it's a lot of work and it's not very comfortable, so I prefer in this case (I mean only if we speak about controller's test) actually getting real data from db. By real data I mean data created with doctrine fixtures. And as long as we don't change database we can depend on the fixtures.
But if we are speaking about changing data inside db (POST/PUT/DELETE methods) I always use mocks. If you'll use em mock and set appropriate expectations on "perist" and "flush" methods, you can be sure that the data is correctly created/updated/deleted actually without any database's modifications.