Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ZF2 - Mocking service requested in Module.php

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');     } } 
like image 398
marcosh Avatar asked Jan 15 '16 12:01

marcosh


2 Answers

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.

Refactoring could be helpful

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

When refactoring is not an option

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.

like image 73
Harmon Wood Avatar answered Sep 20 '22 04:09

Harmon Wood


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.

like image 32
Gabriel Somoza Avatar answered Sep 24 '22 04:09

Gabriel Somoza