2013-03-13 61 views
6

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

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

父::派遣是从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 
+0

您是否尝试过单独运行每个测试? 'dispatch()'在['AbstractControllerTestCase'](https://github.com/zendframework/Component_ZendTest/blob/master/PHPUnit/Controller/AbstractControllerTestCase。php#L239)('Zend \ Test \ PHPUnit \ Controller') – hakre 2013-03-13 10:51:55

+0

它的工作原理!更快,大约15分钟,但我在跳过的测试中遇到错误: – Kudlaty 2013-03-13 12:10:34

+0

PHPUnit_Framework_Exception:PHP致命错误:未收集的异常'Exception''消息''封闭'的序列化不允许'in::44 堆栈跟踪: (0) - (44):serialize(Array) #1-(348):__phpunit_run_isolated_test() #2 {main} – Kudlaty 2013-03-13 12:10:57

回答

2

两个Zend\ServiceManagerZend\EventManager大量使用closures。你不能序列化整个应用程序实例,并希望能够工作,因为它基本上意味着你试图序列化服务工厂和定义为闭包的事件监听器。

该解决方案可能会使用测试Bootstrap.php,如the one of DoctrineORMModule,它不保留内存中的应用程序实例。 这里有一个简单的例子:

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

+0

非常感谢这些优秀的解释! – 2014-01-22 21:58:50