Php Zend Framework 2条令2模块-控制器单元测试中无服务定位器

Php Zend Framework 2条令2模块-控制器单元测试中无服务定位器,php,doctrine-orm,phpunit,zend-framework2,Php,Doctrine Orm,Phpunit,Zend Framework2,这是我的情况。我正在开发Zend Framework 2应用程序。我正在使用Doctrine模块与MySQL数据库通信。它在应用程序中运行良好,我可以从控制器内的服务定位器加载实体管理器 但是在我的控制器单元测试中,服务定位器不存在,因此所有处理数据库的测试都失败,错误消息如下: chdir('/path/to/application/root'); // if you're using composer to install zf2 include_once 'vendor/autoload

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

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

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'));
致命错误:对第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',
        ),
    ),
);
在我的控制器中,我使用以下方法获取实体管理器:

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');
控制器单元测试示例如下所示:

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->getServiceLocator()
返回
NULL


有什么办法解决这个问题吗?

不要在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); 
在测试的设置方法中,只需这样做

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

还将来自事件->应用程序的请求等注入控制器…

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

它是init服务定位器,并执行许多其他控制器的工作。以及成为可用的控制器断言、响应断言、CSS断言等。
示例的细节在这里查看

毕竟我使用了一种稍微不同的方法(但主要思想是相同的,我只是在Bootstrap.php for phpunit中添加了更多的逻辑)。但是你的也可以。我的也做了很多,只是把它简化为基本要求。我也在使用ZF2+Doctrine2,并遵循您的方法。设置ServiceLocator时,事件似乎没有传播到控制器插件——例如,路由器是在ServiceLocator中设置的,而不是在redirect()插件中设置的。有没有关于如何设置的想法?
$this->_controller = new UserTokenController;
$this->_controller->setEvent(self::getMvcEvent());
$this->_controller->setServiceLocator(
    self::getMvcEvent()->getApplication()->getServiceManager()
);