1

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

设置

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

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

关于我的情况有什么选择吗?是否可以在服务管理器中使用服务覆盖工厂,还是我看错了?

4

2 回答 2

1

我认为您需要一个单独的配置文件来进行单元测试。

phpunit.xml

<?xml version="1.0"?>
<phpunit bootstrap="./Bootstrap.php">

引导程序.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随心所欲,甚至是闭包。

于 2017-07-14T10:58:45.883 回答
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);
}
于 2017-07-16T17:21:35.203 回答