使用 Doctrine 在 Symfony2 中测试控制器

2022-01-25 00:00:00 php symfony phpunit symfony-2.1

我在 Symony2 中创建了一个非常简单的 REST 控制器,控制器操作中包含数据库插入/更新/删除.

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?

当前控制器示例:

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()
        );
    }
}

推荐答案

在我看来,你绝对应该避免在测试中更改数据库.

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('DoctrineORMEntityManager')
        ->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).

我在控制器测试中的 GET 方法是,我可以模拟实体存储库等,以便对从 db 获取的每个数据进行存根,但这需要大量工作并且不是很舒服,所以我更喜欢在这种情况下(我的意思是如果我们谈论控制器的测试)实际上从 db 获取真实数据.我所说的真实数据是指用教条固定装置创建的数据.只要我们不改变数据库,我们就可以依赖fixtures.

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.

但是,如果我们谈论更改 db 中的数据(POST/PUT/DELETE 方法),我总是使用模拟.如果您将使用 em mock 并对perist"和flush"方法设置适当的期望,则可以确保在没有任何数据库修改的情况下实际正确创建/更新/删除数据.

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.

相关文章