Тестирование контроллеров в Symfony2 с помощью доктрины
Я создал очень простой контроллер REST в Symony2 с вставкой/обновлением/удалением базы данных в действиях контроллера.
Есть ли хороший способ написать модульные/интеграционные тесты для этих действий контроллера, не загрязняя производственную базу данных? Должен ли я работать с разными средами - или для этого есть предложенный поставщиком фреймворка способ?
Пример контроллера тока:
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;
}
Текущий пример теста:
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()
);
}
}
3 answers
На мой взгляд, вам определенно следует избегать изменения базы данных с помощью ваших тестов.
Мой любимый способ добиться этого - внедрить макет entity manager в тестовый клиент. Например:
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()
);
}
Одна вещь в этом решении, о которой вы должны знать, заключается в том, что вам нужно вводить свой поддельный сервис перед каждым запросом. Это происходит потому, что клиент перезагружает ядро между каждым запросом (что означает, что контейнер также перестраивается).
Редактировать:
Мой ПОЛУЧИТЬ подход в тестах контроллера заключается в том, что я могу имитировать репозитории сущностей и так далее, Чтобы заглушать каждое получение данных из бд, но это большая работа, и это не очень удобно, поэтому я предпочитаю в этом случае (я имею в виду, только если мы говорим о тесте контроллера) на самом деле получать реальные данные из бд. Под реальными данными я подразумеваю данные, созданные с помощью приспособлений доктрины. И до тех пор, пока мы не изменим базу данных, мы можем зависеть от приспособлений.
Но если мы говорим об изменении данных внутри бд (ОПУБЛИКОВАТЬ/ПОМЕСТИТЬ/УДАЛИТЬ методы) Я всегда использую насмешки. Если вы будете использовать макет em и установите соответствующие ожидания для методов "perist" и "flush", вы можете быть уверены, что данные правильно созданы/обновлены/удалены фактически без каких-либо изменений в базе данных.
Вот что я делаю
В вашем тестовом классе добавьте статическую переменную $kernel и создайте функцию, которая загружает ядро в тестовом режиме
protected static $kernel;
protected static $container;
public static function setUpBeforeClass()
{
self::$kernel = new \AppKernel('test', true);
self::$kernel->boot();
self::$container = self::$kernel->getContainer();
}
И в качестве первой строки тестовой функции вызовите self:setUpBeforeClass()
Это заставляет symfony загружать конфигурационный файл config_test.yml, и вы можете определить другое подключение к базе данных там
Хороший способ сделать это, особенно если вам нужно иметь некоторые тестовые данные в вашей базе данных, - использовать отдельную базу данных SQLite вместе с DoctrineFixturesBundle.