I'm having some trouble getting my unit test to work. I'm testing a controller that uses a service that is created by a factory. What I want to achieve is to replace a factory with a mocked service so I can perform tests without using an active database connection.
The setup
In my service manager's configuration file I point to a factory. The factory requires an active database connection that I don't want to use during my unit test.
Namespace MyModule;
return [
'factories' => [
MyService::class => Factory\Service\MyServiceFactory::class,
],
];
Note: I have changed class names and simplified configuration for illustration purposes.
The service uses a mapper that I won't be going into now because that is not relevant to the situation. The mappers are tested in their own testcases. The service itself has it's own testcase as well but needs to be present for the controller's actions to work.
The controller action simply receives information from the service.
Namespace MyModule\Controller;
use MyModule\Service\MyService;
use Zend\Mvc\Controller\AbstractActionController;
class MyController extends AbstractActionController
{
/**
* @var MyService
*/
private $service;
/**
* @param MyService $service
*/
public function __construct(MyService $service)
{
$this->service = $service;
}
/**
* Provides information to the user
*/
public function infoAction()
{
return [
'result' => $this->service->findAll(),
];
}
}
Note: Again, I have changed class names and simplified the example for illustration purposes.
What I've tried
In my testcase I've tried to override the desired factory like this:
/**
* @return \Prophecy\Prophecy\ObjectProphecy|MyService
*/
private function mockService()
{
$service = $this->prophesize(MyService::class);
$service->findAll()->willReturn(['foo', 'bar']);
return $service;
}
/**
* @param \Zend\ServiceManager\ServiceManager $services
*/
private function configureServiceManager(ServiceManager $services)
{
$services->setAllowOverride(true);
$services->setService(MyService::class, $this->mockService()->reveal());
$services->setAllowOverride(false);
}
At first sight this looks great, but it doesn't work. It just seems to append the service to the service manager's list of services, not overriding the factory.
Changing $services->setService to $services->setFactory requires me to build another factory. What I could do is create a factory that injects a mock-mapper into the service but that feels wrong. I'm testing the controller, not the service or mapper so I am trying to avoid complex solutions like that to keep my test cases simple and clear.
Are there any options regarding my situation? Is it possible to override a factory with a service in the service manager or am I looking at it wrong?
I think you need a separate config file for unit testing.
phpunit.xml
Bootstrap.php
config/phpunit.config.phpis a config file created for unit testing only:config/phpunit.config.php
config/phpunit/yourfile.local.php
In
config/phpunit/yourfile.local.phpyou can letMyService::classbe whatever you want, even a closure.