简体   繁体   English

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

[英]How can I unit test a Symfony2 controller?

I want to use Test Driven Development as much as possible — it's a great way of working. 我想尽可能地使用测试驱动开发 - 这是一种很好的工作方式。

I am troubled by the fact that Symfony2 controllers create and return a new Response object. 我对Symfony2控制器创建并返回一个新的Response对象这一事实感到不安。

I want to be able to unit test a controller in isolation. 我希望能够单独测试控制器。

How do you do it? 你怎么做呢?

Is the answer to create a controller as a Plain Old PHP Object, register it as a service and use Dependency Injection to pass a new Response object (or a Response factory) into it? 是否将控制器创建为Plain Old PHP Object,将其注册为服务并使用依赖注入将新的Response对象(或Response工厂)传递给它?

Normally, your controller plugs together different objects and connects them in the correct order. 通常,您的控制器将不同的对象插入在一起并按正确的顺序连接它们。 Maybe he calls a repository, reads some objects and returns them through the render method. 也许他调用一个存储库,读取一些对象并通过render方法返回它们。 Maybe he calls some other Handlers/Managers who do stuff. 也许他会打电话给其他一些做事的其他经理/经理。

This means that a controller is a high level component. 这意味着控制器是高级组件。 More often than not this indicates that functional tests are in order instead of unit tests. 通常情况下,这表明功能测试是按顺序而不是单元测试。 You should not aim to get 100% code coverage with your unit tests. 您不应该通过单元测试获得100%的代码覆盖率。 Maybe you can think of it that way: If you unit test everything the controller calls (model, validation, form, repository), what could go wrong? 也许你可以这样想:如果你对控制器调用的所有内容(模型,验证,表单,存储库)进行单元测试,那么可能出现什么问题? Most of the time it's something you only observe when using all the real classes involved when in production. 大多数时候,只有在生产中使用所有真正的类时才会观察到它。

I want also like to point out that TDD does not mean that everything has to be unit-tested. 我还想指出,TDD并不意味着一切都必须经过单元测试。 It's ok to have some functional tests for the high-level code. 可以对高级代码进行一些功能测试。 As said, if you test the low-level components with unit-tests you should only test how they are working together which you cannot test with mocks because you tell the mocks what the return value is. 如上所述,如果使用单元测试测试低级组件,则只应测试它们如何协同工作,而不能使用模拟测试,因为您告诉模拟返回值是什么。

If your controller does more than plugging parts of the system together you should think about refactoring the stuff into more low-level classes you can test with unit-tests. 如果您的控制器不仅仅是将系统的各个部分插在一起,那么您应该考虑将这些内容重构为更多的低级类,您可以使用单元测试进行测试。

So my suggestion would be to use functional tests to test your controllers and use unit-tests to test your models and your business logic stuff. 所以我的建议是使用功能测试来测试你的控制器并使用单元测试来测试你的模型和你的业务逻辑。

If you struggle with functional tests, you can read the following: 如果您在使用功能测试时遇到困难,可以阅读以下内容:

Use mocks to isolate models and other objects from main controller method's logic, see http://www.phpunit.de/manual/3.7/en/test-doubles.html#test-doubles.mock-objects 使用模拟从主控制器方法的逻辑中隔离模型和其他对象,请参阅http://www.phpunit.de/manual/3.7/en/test-doubles.html#test-doubles.mock-objects

I think that in older versions you could mock entire class, but with latest phpunit 3.6.10 that i have it doesn't seem to work. 我认为在旧版本中你可以模拟整个类,但最新的phpunit 3.6.10,我有它似乎不起作用。 So i guess you are left with depency injection pattern 所以我猜你会留下依赖注入模式

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

Unit Testing 单元测试

Refactor your controllers to be services: http://symfony.com/doc/current/cookbook/controller/service.html 将您的控制器重构为服务: http//symfony.com/doc/current/cookbook/controller/service.html

Then you can easily unit test them. 然后你可以轻松地对它们进行单元测

Functional Testing 功能测试

Of course (as already mentioned by others) you can use the WebTestCase as described here: http://symfony.com/doc/current/book/testing.html#functional-tests 当然(正如其他人已经提到的那样)你可以使用这里描述的WebTestCasehttpWebTestCase

Lewis - I thought I'd jump in here. 刘易斯 - 我以为我会跳进这里。 The approach above has you replicating the better part of your actions logic in your tests. 上面的方法让您在测试中复制动作逻辑的更好部分。 There's nothing wrong with this, many frameworks (notably RSPEC in Rails) actually suggest you perform both Unit tests on your Controller objects as well as functional tests. 这没有什么不对,很多框架(特别是Rails中的RSPEC)实际上建议你对Controller对象进行单元测试以及功能测试。 However, given your example, I think I'd skip the unit test and go for the functional approach. 但是,根据你的例子,我想我会跳过单元测试并去寻找功能方法。

The point of a test in my mind is to create a sandboxed environment, run the test, and check for side effects and direct result. 在我看来,测试的重点是创建一个沙盒环境,运行测试,并检查副作用和直接结果。 If you get to a point where most of your test is isolating the method, then its probably time for either a different testing approach or a different approach to writing your class. 如果你达到了大部分测试都是隔离方法的程度,那么可能是时候采用不同的测试方法或者编写类的不同方法。 Given that this is a controller, and by nature they glue together different pieces of the stack, I'd create your sandbox farther up the stack. 鉴于这是一个控制器,并且本质上它们将不同的堆栈部分粘合在一起,我会在堆栈中创建更远的沙箱。 Specifically, I would use an approach like this: 具体来说,我会使用这样的方法:

https://github.com/PolishSymfonyCommunity/SymfonyMockerContainer https://github.com/PolishSymfonyCommunity/SymfonyMockerContainer

Worked great for me :) 为我工作很棒:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM