我在单元测试工作时遇到了一些麻烦。我正在测试一个使用工厂创建的服务的控制器。我想要实现的是用模拟服务替换工厂,这样我就可以在不使用活动数据库连接的情况下执行测试。
在我的服务经理的配置文件中,我指向一个工厂。 工厂需要一个我不想在单元测试期间使用的活动数据库连接。
Namespace MyModule;
return [
'factories' => [
MyService::class => Factory\Service\MyServiceFactory::class,
],
];
注意:为了便于说明,我更改了类名和简化配置。
该服务使用的是我现在不会进入的映射器,因为这与情况无关。映射器在他们自己的测试用例中进行测试。服务本身也有它自己的测试用例,但需要存在才能使控制器的工作起作用。
控制器操作只是从服务中接收信息。
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(),
];
}
}
注意:我再次更改了类名并简化了示例以供说明之用。
在我的测试用例中,我试图像这样覆盖所需的工厂:
/**
* @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);
}
乍一看,这看起来很棒,但它不起作用。它似乎只是将服务附加到服务管理器的服务列表中,而不是覆盖工厂。
将$services->setService
更改为$services->setFactory
需要我建立另一家工厂。我能做的是创建一个工厂,将一个模拟映射器注入服务,但感觉不对。我正在测试控制器,而不是服务或映射器,所以我试图避免像这样的复杂解决方案,以保持我的测试用例简单明了。
我的情况有没有选择?是否可以在服务管理器中使用服务覆盖工厂,或者我看错了吗?
答案 0 :(得分:1)
我认为您需要一个单独的配置文件进行单元测试。
<强> phpunit.xml 强>
<?xml version="1.0"?>
<phpunit bootstrap="./Bootstrap.php">
<强> bootstrap.php中强>
require 'vendor/autoload.php';
$configuration = include 'config/phpunit.config.php';
Zend\Mvc\Application::init ($configuration);
config/phpunit.config.php
是仅为单元测试创建的配置文件:
<强>配置/ phpunit.config.php 强>
$configuration = include (__DIR__ . '/application.config.php');
$configuration ['module_listener_options'] ['config_glob_paths'] [] = 'config/phpunit/{,*.}local.php';
<强>配置/ PHPUnit的/ yourfile.local.php 强>
return [
'service_manager' => array (
'factories' => [
MyService::class => ...
]
)
];
在config/phpunit/yourfile.local.php
中,您可以让MyService::class
成为您想要的任何内容,即使是关闭。
答案 1 :(得分:0)
没有必要为此建立新工厂。只需使用简单的闭包:
/**
* @param \Zend\ServiceManager\ServiceManager $services
*/
private function configureServiceManager(ServiceManager $services)
{
$services->setAllowOverride(true);
$mockedService = $this->mockService();
$services->setFactory(MyService::class, function() use ($mockedService) {
$mockedService->reveal();
});
$services->setAllowOverride(false);
}
现在您仍然可以仅模拟所需的服务。在测试用例中增加期望仍然应该是灵活的:
public function testMyCase()
{
$expected = ['foo', 'bar'];
$this->mockService()->findAll()->willReturn($expected);
$result = $this->service->findAll();
$this->assertSame($expected, $result);
}