Тестирование контроллеров в 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()
        );
    }
}
Author: madflow, 2013-01-19

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", вы можете быть уверены, что данные правильно созданы/обновлены/удалены фактически без каких-либо изменений в базе данных.

 18
Author: Cyprian, 2013-01-19 13:49:58

Вот что я делаю

В вашем тестовом классе добавьте статическую переменную $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, и вы можете определить другое подключение к базе данных там

 4
Author: Sethunath, 2013-01-19 13:18:22

Хороший способ сделать это, особенно если вам нужно иметь некоторые тестовые данные в вашей базе данных, - использовать отдельную базу данных SQLite вместе с DoctrineFixturesBundle.

 1
Author: Patrick, 2017-05-23 11:54:07