I am trying to test a controller of my ZF2 application. Suppose this controller is in my A
module.
In the onBootstrap
method of the Module.php
of the module A
I am using the service manager to retrieve a service of another module, say B
, that I am not loading.
How can set a mock of the requested service in the service manager? Mind that I can not use $this->getApplicationServiceLocator()
to do this in my test, since this is already calling the Module.onBootstrap
method of my A
module.
To post some code, this is what I am doing at the moment
bootstrap.php
namespace Application; use Zend\Mvc\Service\ServiceManagerConfig; use Zend\ServiceManager\ServiceManager; use RuntimeException; class Bootstrap { protected static $serviceManager; public static function init() { $modulePath = static::findParentPath('module'); $vendorPath = static::findParentPath('vendor'); if (is_readable($vendorPath . '/autoload.php')) { $loader = include $vendorPath . '/autoload.php'; } else { throw new RuntimeException('Cannot locate autoload.php'); } $config = [ 'modules' => [ 'Application', ], 'module_listener_options' => [ 'module_paths' => [ $modulePath, $vendorPath ] ] ]; $serviceManager = new ServiceManager(new ServiceManagerConfig()); $serviceManager->setService('ApplicationConfig', $config); $serviceManager->get('ModuleManager')->loadModules(); static::$serviceManager = $serviceManager; } protected static function findParentPath($path) { $dir = __DIR__; $previousDir = '.'; while (!is_dir($dir . '/' . $path)) { $dir = dirname($dir); if ($previousDir === $dir) { return false; } $previousDir = $dir; } return $dir . '/' . $path; } public static function getServiceManager() { return static::$serviceManager; } } Bootstrap::init();
my actual test class
namespace Application\Functional; use Application\Bootstrap; use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase; class ValidateCustomerRegistrationTest extends AbstractHttpControllerTestCase { public function setUp() { $serviceManager = Bootstrap::getServiceManager(); $applicationConfig = $serviceManager->get('ApplicationConfig'); $this->setApplicationConfig($applicationConfig); parent::setUp(); } public function testRegisterValidUserWithOnlyEquomobiliData() { $this->getApplicationServiceLocator(); } }
Module.php simplified
namespace Application Class Module { public function onBootstrap(MvcEvent $e) { $serviceManager = $e->getApplication()->getServiceManager(); $service = $serviceManager->get('Service\From\Other\Module'); } }
There is not enough data here to help you directly. It would be more useful to have the function ValidateCustomerRegistrationTest->getApplicationServiceLocator()
for starters.
I hope I can help you indirectly.
When I am writing a unit test I start with a few personal rules.
Only test the code being testing. Mock EVERYTHING else. No need to test something that should have it's own tests already.
How a function works should not be important. Only the input/output. This keeps your test viable even when the core of a function changes drastically.
/** * @param MvcEventInterface $event * @param Service\From\Other\ModuleInterface $service * * @return boolean */ public function onBootstrap(MvcEventInterface $event, Service\From\Other\ModuleInterface $service) { return true; }
Then in the test class:
public function testOnBootstrap(){ $eventMock = $this->getMock(MvcEventInterface::class); $serviceMock = $this->getMock(ModuleInterface::class); $module = new Module(); $result = $module->onBootstrap($eventMock, $serviceMock); $this->assertTrue($result); }
* I clearly do not know what you are trying to test
There are two mock types that can help out, that occur to me right off the bat, mock, and mockBuilder. Take a look at the PHPUnit documentation for Test Doubles.
$serviceMock = $this->getMockBuilder(ServiceManager::class) ->disableOriginalConstructor() ->setMethods([ '__construct', 'get' ]) ->getMock(); $serviceMock ->expects($this->any()) ->method('get') ->will($this->returnValue( $this->getMock(ModuleManagerInterface::class) ));
Good luck, I hope you can let us know if we can help you more specifically. I would also recommend looking into SOLID Principles of Object Oriented Programing. One of many programming principles that should make your code clean, easy to extend, and easy to test.
One way to accomplish what you're trying to do may be to force an overwrite of the service in the Service Manager before dispatching the request to your test controller. Here's an example of how to do that (NOTE: since what you're doing is during the module's bootstrap process, the example may not translate 100% to your situation).
Like other people mentioned: you may still want to double-check whether you can refactor to a cleaner approach, but that's another story.
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