Php 在Symfony2中使用条令测试控制器

Php 在Symfony2中使用条令测试控制器,php,symfony,phpunit,symfony-2.1,Php,Symfony,Phpunit,Symfony 2.1,我在Symony2中创建了一个非常简单的REST控制器,在控制器操作中插入/更新/删除数据库 有没有一种很好的方法可以在不污染生产数据库的情况下为这些控制器操作编写单元/集成测试?我是否必须使用不同的环境?或者框架供应商是否为此提出了建议 当前控制器示例: public function postAction() { $json = $this->getRequest()->getContent(); $params = json_decode($json);

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

有没有一种很好的方法可以在不污染生产数据库的情况下为这些控制器操作编写单元/集成测试?我是否必须使用不同的环境?或者框架供应商是否为此提出了建议

当前控制器示例:

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

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

我最喜欢的实现方法是在测试客户机中注入实体管理器mock。例如:

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()
    );
}
对于这个解决方案,您应该注意的一点是,您需要在每个请求之前注入模拟服务。这是因为客户机在每个请求之间重新启动内核(这意味着容器也在重建)

编辑:

我在controller测试中的GET方法是,我可以模拟实体存储库等,以便存根从db获取的每个数据,但这需要大量的工作,而且不是很舒服,所以我更喜欢在这种情况下(我的意思是,只有在我们谈到controller的测试时)从db实际获取真实数据。我所说的真实数据指的是用计算机创建的数据。只要我们不改变数据库,我们就可以依靠固定装置

但是,如果我们谈论的是在db(POST/PUT/DELETE方法)中更改数据,我总是使用mock。如果您使用em mock并对“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数据库和doctrineTextureBundle


您绝对不应该在生产数据库上运行单元测试。你应该有一个测试数据库代替@真的!我不确定Symfony2中的开发和部署过程会是什么样子。我如何告诉phpunit在Symfony2中使用哪个数据库环境…?检查,特别是最后关于测试配置的部分。我喜欢!但是,当实际上没有表示时,如何测试PUT/DELETE/GET请求呢?例如,GET/service/sandbox/1应该获取ID为1的沙盒。在我面向数据库的思想中,我首先创建资源和GET/PUT并删除它!谢谢我已经编辑了我的答案,我希望这能带来更多的澄清。最好的!:)尝试和。。。就像一个符咒!没有意识到您还可以通过编程方式设置服务。我总是使用容器来获取它们,并且只在配置文件中建立它们a)和单元测试中建立它们b)(没有容器,只是将模拟传递给需要它的构造函数)。在功能测试中伪造内核的好技巧,确保这在许多其他控制器测试中有用,例如防止发送邮件或防止访问远程服务。这个黑客大大简化了功能测试!!!Thnx@CyprianHey工作正常,但现在失败了,如果我添加FOSUserBundle和HWIOAuthBundle!!!它说
PHP致命错误:在第759行的/files/custom\u www/antique crayon/preproduction/vendor/doctrine/orm/lib/doctrine/orm/EntityManager.PHP中对非对象调用成员函数getRepository()-如果我停止注入
doctrine.orm.default\u entity\u manager
(当然,并注释掉期望值一次)然后它给出了绿色条(当然,然后保存到真正的DB,这是我们想要避免的)