PHP ZF2单元测试调度方法非常慢

时间:2013-03-13 10:44:11

标签: php unit-testing phpunit zend-framework2 zend-test

我需要测试用ZF2编写的大型网站。有443个测试和大约10000个断言。使用代码覆盖测试需要6个小时! 我想我发现了问题:在控制器的测试中,我使用了AbstractHttpControllerTestCase的调度方法。每次测试后,调度方法的执行时间都会增加(从秒到数十秒)。

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

我的调度方法:

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是AbstractHttpControllerTestCase的方法。

测试样本:

$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);

请帮忙。 感谢。


更新

我使用进程隔离并在大约15分钟内完成测试(没有代码覆盖率)但是我在测试中得到了标记为跳过的错误:

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

1 个答案:

答案 0 :(得分:2)

Zend\ServiceManagerZend\EventManager都大量使用closures。您无法序列化整个应用程序实例并希望它能够正常工作,因为它基本上意味着您尝试序列化定义为闭包的服务工厂和事件侦听器。

解决方案可能是使用类似the one of DoctrineORMModule的测试Bootstrap.php,它不会将应用程序实例保留在内存中。 这是一个简化的例子:

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

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

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

unset($appConfig);

TestConfiguration应该看起来像standard mvc application's config

您还需要ServiceManagerFactory。可以找到示例实现herehere

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

现在,无论您在测试中想要什么,都可以:

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

$application->bootstrap();

// ...

通过此设置,您可以在绝缘中运行测试。

另一方面,您应该首先关注实际的单元测试,因为ZF2确实简化了如何组合复杂对象。您还应该正确设置覆盖率过滤器,以便不处理不相关的代码(可能会耗费大量时间)。

此外,重新使用mvc应用程序实例是错误的,因为帮助程序不是无状态的,这使得重用它们变得很困难。