如何对Symfony2控制器进行单元测试?

时间:2012-04-12 15:30:24

标签: php unit-testing symfony

我希望尽可能多地使用测试驱动开发 - 这是一种很好的工作方式。

Symfony2控制器创建并返回一个新的Response对象,我感到很困扰。

我希望能够单独对控制器进行单元测试。

你是怎么做到的?

是否将控制器创建为普通旧PHP对象,将其注册为服务并使用依赖注入将新的Response对象(或Response工厂)传递到其中? / p>

4 个答案:

答案 0 :(得分:53)

通常,您的控制器将不同的对象插入在一起,并按正确的顺序连接它们。也许他调用一个存储库,读取一些对象并通过render方法返回它们。也许他会打电话给其他一些做事的处理员/经理。

这意味着控制器是高级组件。通常情况下,这表明功能测试是按顺序而不是单元测试。您不应该通过单元测试获得100%的代码覆盖率。也许你可以这样想:如果你对控制器调用的所有内容(模型,验证,表单,存储库)进行单元测试,那么可能出现什么问题?大多数情况下,只有在使用生产中涉及的所有真实类时才会观察到这一点。

我还想指出TDD并不意味着一切都必须经过单元测试。可以对高级代码进行一些功能测试。如上所述,如果使用单元测试测试低级组件,则只应测试它们如何协同工作,而不能使用模拟测试,因为您告诉模拟返回值是什么。

如果您的控制器不仅仅是将系统的各个部分插在一起,那么您应该考虑将这些内容重构为更多可以通过单元测试进行测试的低级类。

所以我的建议是使用功能测试来测试你的控制器并使用单元测试来测试你的模型和你的业务逻辑。

如果您在进行功能测试时遇到困难,可以阅读以下内容:

答案 1 :(得分:2)

使用模拟从主控制器方法的逻辑中隔离模型和其他对象,请参阅http://www.phpunit.de/manual/3.7/en/test-doubles.html#test-doubles.mock-objects

我认为在旧版本中你可以模拟整个课程,但是最新的phpunit 3.6.10我认为它似乎不起作用。所以我猜你会留下依赖注入模式

class objss{
    function ss(){
        $x = new zz();
        var_dump($x->z());
    }
}



class MoTest extends PHPUnit_Framework_TestCase{
    public function setUp(){

    }

    public function testA(){
        $class = $this->getMock('zzMock', array('z'), array(), 'zz');
        $class->expects($this->any())->method('z')->will($this->returnValue('2'));

        $obj = new objss();
        $this->assertEquals('2', $obj->ss());
    }
}

答案 2 :(得分:1)

单元测试

将您的控制器重构为服务: http://symfony.com/doc/current/cookbook/controller/service.html

然后你可以轻松地对它们进行单元测试。

功能测试

当然(正如其他人已经提到的那样)您可以使用此处所述的WebTestCasehttp://symfony.com/doc/current/book/testing.html#functional-tests

答案 3 :(得分:0)

刘易斯 - 我以为我会跳进这里。上面的方法让您在测试中复制动作逻辑的更好部分。这没有什么不对,很多框架(特别是Rails中的RSPEC)实际上建议你对Controller对象进行单元测试以及功能测试。但是,根据你的例子,我想我会跳过单元测试并去寻找功能方法。

在我看来,测试的重点是创建一个沙盒环境,运行测试,并检查副作用和直接结果。如果你达到了大部分测试都是隔离方法的程度,那么可能是时候采用不同的测试方法或者编写类的不同方法。鉴于这是一个控制器,并且本质上它们将不同的堆栈部分粘合在一起,我会在堆栈中创建更远的沙箱。具体来说,我会使用这样的方法:

https://github.com/PolishSymfonyCommunity/SymfonyMockerContainer

非常适合我:)