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

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

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

我使用 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

最佳答案

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 应用程序实例是错误的,因为助手不是无状态的,这使得重用它们变得困难。

关于PHP ZF2 单元测试调度方法非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15383073/

相关文章:

php - 如何根据以前的答案确定在测验中向用户询问哪些问题

php - 使用 LIKE 子句搜索 Mysql

ruby-on-rails - 如何使用 Unit::Test 在 Ruby on Rails 3.0.7 中运行单个测试?

c# - 如何使用 cake(c# make) 脚本在 xunit 中通过和失败测试用例计数

php - 如何跳过 PHPUnit 中的错误测试?

PHPUnit - 模拟特征

PHP:来自远程服务器的 Http 请求失败

java - @EJB 注释不适用于 EJB 应用程序中的初始化 bean

php - 如何使 PHPunit 在警告时返回非零退出状态

php - mysql如何获取只有一列的值?