Here is my situation. I am developing a Zend Framework 2 application. I am using Doctrine module to communicate with a MySQL database. It is working fine in the application, I can load the entity manager from the service locator inside my controllers.
But inside my controller unit tests, the service locator doesn't exists and therefor all tests dealing with the database are failing with error message like this:
Fatal error: Call to a member function
get()
on a non-object in /Users/richardknop/Projects/myproject/module/Api/src/Api/Controller/UserController.php on line 19
I have narrowed the problem down to this method:
$this->getServiceLocator()
Which works in my controllers but returns NULL in unit tests.
This is my 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',
),
),
);
My local.php file with database connection details:
<?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',
),
),
),
),
);
In my module.config.php I have:
'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'
),
),
),
),
And in my controllers I have this method to get the entity manager:
private $_em;
private function _getEntityManager()
{
if (null === $this->_em) {
$this->_em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
}
return $this->_em;
}
Now my unit tests. Here is the bootstrap file:
<?php
chdir(dirname(__DIR__));
include __DIR__ . '/../init_autoloader.php';
return Zend\Mvc\Application::init(include 'config/application.config.php');
And setUp method of my controller tests looks like:
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);
}
An example controller unit test then looks like:
public function testGetListHttpStatusCode()
{
$response = $this->_controller->dispatch($this->_request);
$this->assertEquals(405, $response->getStatusCode());
}
All tests that are not connecting to the database are passing. Tests where I am using entity manager are failing though with this error:
Fatal error: Call to a member function
get()
on a non-object in /Users/richardknop/Projects/myproject/module/Api/src/Api/Controller/UserController.php on line 19
This is line 19:
$this->_em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
And the reason it's failing is that $this->getServiceLocator()
returns NULL
.
Any ideas how to solve this issue?
Just extend your controller from \Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase
It's init service locator and do a lot of other Controller's job. As well become available controller asserts, response asserts, CSS asserts etc. Details with examples look here http://framework.zend.com/manual/2.1/en/modules/zend.test.phpunit.html
dont' do this ins your bootstrap.php
return Zend\Mvc\Application::init(include 'config/application.config.php');` in your
but something like this:
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'));
you could then create static methods in your test-case to set and get the event...
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;
}
}
...and inject it from the bootstrap.php file
My\TestCase::setMvcEvent($event);
and in your setUp method of the test simply do something like this
$this->_controller = new UserTokenController;
$this->_controller->setEvent(self::getMvcEvent());
$this->_controller->setServiceLocator(
self::getMvcEvent()->getApplication()->getServiceManager()
);
also inject request etc. from event->application into controller here...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With