简体   繁体   English

PHP ZF2单元测试分发方法非常慢

[英]PHP ZF2 Unit Tests dispatch method very slow

I need to test a big site written in ZF2. 我需要测试一个用ZF2编写的大型网站。 There is 443 test and about 10000 assertions. 有443个测试和大约10000个断言。 Testing with code coverage takes 6 hours! 代码覆盖率测试需要6个小时! I think I found the problem: In controller's tests I use a dispatch method from AbstractHttpControllerTestCase. 我想我发现了问题:在控制器的测试中,我使用了AbstractHttpControllerTestCase中的调度方法。 Time of execution of dispatch method is increasing after each test (from fractions of second to tens of seconds). 每次测试后,执行分派方法的时间会增加(从几分之一秒到数十秒)。

I use ZF 2.1.3, PHPUnit 3.7, PHP_CodeCoverage 1.2, Xdebug v2.2.1, PHP 5.4.7. 我使用ZF 2.1.3,PHPUnit 3.7,PHP_CodeCoverage 1.2,Xdebug v2.2.1,PHP 5.4.7。

My dispatch method: 我的调度方法:

public function dispatch($url, $method = HttpRequest::METHOD_GET, $params = array())
{
    $s = microtime(true);

    parent::dispatch($url, $method, $params);

    $end = microtime(true) - $s;
    echo 'dis: '.$end."\n";

    return $this->getApplication()->getMvcEvent()->getResult();
}

parent::dispatch is method from AbstractHttpControllerTestCase. parent :: dispatch是AbstractHttpControllerTestCase中的方法。

Sample of test: 测试样本:

$result = $this->dispatch('/archive/finance/older');

$this->assertControllerName('skycontent\controller\article');
$this->assertActionName('archive');
$this->assertParamValue('older', true);
$this->assertParamValue('category', 'finance');

$vars = (array) $result->getVariables();

$this->assertArrayHasKey('archivePosts', $vars);

Please help. 请帮忙。 Thanks. 谢谢。


Update: 更新:

I use process isolation and tests done in about 15 minutes (without code coverage) but I get error in the test that are marked as skipped: 我使用进程隔离和测试在大约15分钟内完成(没有代码覆盖),但是在测试中我得到了标记为已跳过的错误:

PHPUnit_Framework_Exception: PHP Fatal error:  Uncaught exception 'Exception' with message 'Serialization of 'Closure' is not allowed' in -:44

Both Zend\\ServiceManager and Zend\\EventManager make heavy use of closures . Zend\\ServiceManagerZend\\EventManager都大量使用闭包 You cannot serialize an entire application instance and expect that to work, since it would basically mean that you attempt to serialize service factories and event listeners defined as closures. 您不能序列化整个应用程序实例并期望它能正常工作,因为这基本上意味着您将尝试序列化定义为闭包的服务工厂和事件侦听器。

The solution may be to use a test Bootstrap.php like the one of DoctrineORMModule , which doesn't keep an application instance in memory. 解决方案可能是使用测试Bootstrap.php的DoctrineORMModule之一 ,它不会将应用程序实例保留在内存中。 Here's a simplified example: 这是一个简化的示例:

require_once __DIR__ . '/../vendor/autoload.php';

$appConfig = require __DIR__ . '/TestConfiguration.php';

\YourModuleTest\Util\ServiceManagerFactory::setConfig($appConfig);

unset($appConfig);

(the TestConfiguration should look like a standard mvc application's config ) TestConfiguration应该看起来像一个标准的mvc应用程序的config

You also need the ServiceManagerFactory . 您还需要ServiceManagerFactory Sample implementation can be found here and here . 示例实现可在此处此处找到。

namespace YourModuleTest\Util;

class ServiceManagerFactory
{
    /**
     * @var array
     */
    protected static $config;

    /**
     * @param array $config
     */
    public static function setConfig(array $config)
    {
        static::$config = $config;
    }

    /**
     * Builds a new service manager
     * Emulates {@see \Zend\Mvc\Application::init()}
     */
    public static function getServiceManager()
    {
        $serviceManager = new ServiceManager(new ServiceManagerConfig(
            isset(static::$config['service_manager']) 
                ? static::$config['service_manager'] 
                : array()
        ));

        $serviceManager->setService('ApplicationConfig', static::$config);
        $serviceManager->setFactory(
            'ServiceListener',
            'Zend\Mvc\Service\ServiceListenerFactory'
        );

        /** @var $moduleManager \Zend\ModuleManager\ModuleManager */
        $moduleManager = $serviceManager->get('ModuleManager');
        $moduleManager->loadModules();

        return $serviceManager;
    }
}

Now, wherever you want in your tests, you can: 现在,无论您要在哪里进行测试,都可以:

$serviceManager = \YourModuleTest\Util\ServiceManagerFactory::getServiceManager();
$application    = $serviceManager->get('Application');

$application->bootstrap();

// ...

With this setup, you can run tests in insulation. 使用此设置,您可以在绝缘中运行测试。

On the other side, you should focus on real unit tests first, since ZF2 really simplifies how you can compose complex objects. 另一方面,您应该首先关注真实的单元测试,因为ZF2确实简化了组成复杂对象的方式。 You should also correctly setup the coverage filters so that unrelated code is not handled (that may eat up a lot of time). 您还应该正确设置coverage筛选器,以便不处理不相关的代码(这可能会花费很多时间)。

Also, re-using the mvc application instance is wrong, since the helpers are not stateless, which makes it hard to reuse them. 同样,重用mvc应用程序实例是错误的,因为助手不是无状态的,这使得重用它们变得困难。

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

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