对象模拟 - 如何在服务管理器中用服务替换工厂?

时间:2017-07-14 09:35:28

标签: php unit-testing phpunit zend-framework3 zend-test

我在单元测试工作时遇到了一些麻烦。我正在测试一个使用工厂创建的服务的控制器。我想要实现的是用模拟服务替换工厂,这样我就可以在不使用活动数据库连接的情况下执行测试。

设置

在我的服务经理的配置文件中,我指向一个工厂。  工厂需要一个我不想在单元测试期间使用的活动数据库连接。

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需要我建立另一家工厂。我能做的是创建一个工厂,将一个模拟映射器注入服务,但感觉不对。我正在测试控制器,而不是服务或映射器,所以我试图避免像这样的复杂解决方案,以保持我的测试用例简单明了。

我的情况有没有选择?是否可以在服务管理器中使用服务覆盖工厂,或者我看错了吗?

2 个答案:

答案 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);
}