【问题标题】:Zend Framework 2 Doctrine 2 Module - No Service Locator in Controller Unit TestsZend Framework 2 Doctrine 2 模块 - 控制器单元测试中没有服务定位器
【发布时间】:2012-09-08 01:33:46
【问题描述】:

这是我的情况。我正在开发 Zend Framework 2 应用程序。我正在使用 Doctrine 模块与 MySQL 数据库进行通信。它在应用程序中运行良好,我可以从控制器内的服务定位器加载实体管理器。

但在我的控制器单元测试中,服务定位器不存在,因此所有处理数据库的测试都失败,并显示如下错误消息:

致命错误:在第 19 行的 /Users/richardknop/Projects/myproject/module/Api/src/Api/Controller/UserController.php 中的非对象上调用成员函数 get()

我已将问题缩小到这种方法:

$this->getServiceLocator()

在我的控制器中有效,但在单元测试中返回 NULL。

这是我的 application.config.php:

<?php

return array(
    'modules' => array(
        'DoctrineModule',
        'DoctrineORMModule',
        'Api',
    ),
    'module_listener_options' => array(
        'config_glob_paths' => array(
            'config/autoload/{,*.}{global,local}.php',
        ),
        'module_paths' => array(
            './module',
            './vendor',
        ),
    ),
);

包含数据库连接详细信息的 local.php 文件:

<?php

return array(
    'doctrine' => array(
        'connection' => array(
            'orm_default' => array(
                'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
                'params' => array(
                    'host'     => 'localhost',
                    'port'     => '3306',
                    'user'     => 'root',
                    'password' => 'root',
                    'dbname'   => 'mydb',
                ),
            ),
        ),
    ),
);

在我的 module.config.php 中有:

'doctrine' => array(
    'driver' => array(
        __NAMESPACE__ . '_driver' => array(
            'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
            'cache' => 'array',
            'paths' => array(__DIR__ . '/../src/' . __NAMESPACE__ . '/Entity')
        ),
        'orm_default' => array(
            'drivers' => array(
                __NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
            ),
        ),
    ),
),

在我的控制器中,我有这种方法来获取实体管理器:

private $_em;

private function _getEntityManager()
{
    if (null === $this->_em) {
        $this->_em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
    }
    return $this->_em;
}

现在我的单元测试。这是引导文件:

<?php

chdir(dirname(__DIR__));
include __DIR__ . '/../init_autoloader.php';
return Zend\Mvc\Application::init(include 'config/application.config.php');

我的控制器测试的 setUp 方法如下所示:

public function setUp()
{
    $this->_controller = new UserTokenController;
    $this->_request = new Request;
    $this->_routeMatch = new RouteMatch(array('controller' => 'user'));
    $this->_event = new MvcEvent();
    $this->_event->setRouteMatch($this->_routeMatch);
    $this->_controller->setEvent($this->_event);
}

一个示例控制器单元测试如下所示:

public function testGetListHttpStatusCode()
{
    $response = $this->_controller->dispatch($this->_request);
    $this->assertEquals(405, $response->getStatusCode());
}

所有未连接到数据库的测试都通过了。我使用实体管理器的测试失败,但出现此错误:

致命错误:在第 19 行的 /Users/richardknop/Projects/myproject/module/Api/src/Api/Controller/UserController.php 中的非对象上调用成员函数 get()

这是第 19 行:

$this->_em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');

它失败的原因是$this-&gt;getServiceLocator()返回NULL

任何想法如何解决这个问题?

【问题讨论】:

    标签: php doctrine-orm phpunit zend-framework2


    【解决方案1】:

    不要在你的 bootstrap.php 中这样做

    return Zend\Mvc\Application::init(include 'config/application.config.php');` in your 
    

    但是像这样:

    chdir('/path/to/application/root');
    
    // if you're using composer to install zf2
    include_once 'vendor/autoload.php';
    // if not using composer initialize your custom autoloading here
    
    $configuration = include('config/application.config.php');
    $serviceManager = new ServiceManager(new ServiceManagerConfig(
        isset($configuration['service_manager']) ? $configuration['service_manager'] : array()
    ));
    $serviceManager->setService('ApplicationConfig', $configuration);
    $serviceManager->setFactory('ServiceListener', 'Zend\Mvc\Service\ServiceListenerFactory');
    
    $moduleManager = $serviceManager->get('ModuleManager');
    $moduleManager->loadModules();
    $serviceManager->setAllowOverride(true);
    
    $application = $serviceManager->get('Application');
    $event  = new MvcEvent();
    $event->setTarget($application);
    $event->setApplication($application)
        ->setRequest($application->getRequest())
        ->setResponse($application->getResponse())
        ->setRouter($serviceManager->get('Router'));
    

    然后您可以在测试用例中创建静态方法来设置和获取事件...

    namespace My;
    
    class TestCase extends \Some\Basic\TestCase
    {
        protected static $_mvcEvent;
    
        public static function setMvcEvent(MvcEvent $e) {
    
            self::$_mvcEvent = $e;
        }
    
        public static function getMvcEvent() {
    
            return self::$_mvcEvent;
        }
    }
    

    ...并从 bootstrap.php 文件中注入它

    My\TestCase::setMvcEvent($event); 
    

    在你的测试的 setUp 方法中简单地做这样的事情

    $this->_controller = new UserTokenController;
    $this->_controller->setEvent(self::getMvcEvent());
    $this->_controller->setServiceLocator(
        self::getMvcEvent()->getApplication()->getServiceManager()
    );
    

    也将来自事件->应用程序的请求等注入到这里的控制器中......

    【讨论】:

    • 毕竟我使用了一些不同的方法(但主要思想是相同的,我只是在我的 Bootstrap.php 中为 phpunit 放置了更多逻辑)。但你的也可以。
    • 我的也做了很多,只是减少到基本要求
    • 你好安德烈亚斯。我也在使用 ZF2+Doctrine2 并遵循您的方法。虽然设置了 ServiceLocator,但似乎事件没有传播到控制器插件 - 例如,路由器是在 serviceLocator 中设置的,而不是在 redirect() 插件中设置的。关于如何设置的任何想法?
    【解决方案2】:

    只需从 \Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase

    它是初始化服务定位器,可以完成很多其他控制器的工作。以及可用的控制器断言、响应断言、CSS 断言等。 带有示例的详细信息请看这里http://framework.zend.com/manual/2.1/en/modules/zend.test.phpunit.html

    【讨论】:

      猜你喜欢
      • 2013-08-05
      • 2013-09-16
      • 1970-01-01
      • 2012-12-25
      • 1970-01-01
      • 1970-01-01
      • 2013-09-18
      • 1970-01-01
      • 2012-09-17
      相关资源
      最近更新 更多