如何在功能测试中使用symfony 4模拟服务?



我有一个命令总线处理程序,它注入了一些服务:

class SomeHandler
{
private $service;
public function __construct(SomeService $service)
{
$this->service = $service;
}
public test(CommandTest $command)
{
$this->service->doSomeStuff();
}
}

SomeService有带有外部调用的方法doSomeStuff,我不希望在测试期间使用。

class SomeService
{
private $someBindedVariable;
public function __construct($someBindedVariable)
{
$this->someBindedVariable = $someBindedVariable;
}
public function doSomeStuff()
{
//TODO: some stuff
}
}

在测试中,我尝试用模拟对象替换服务

public function testTest()
{
$someService = $this->getMockBuilder(SomeService::class)->getMock();
$this->getContainer()->set(SomeService::class, $someService);
//TODO: functional test for the route, which uses SomeHandler
}

第一个问题是此代码将抛出异常"App\Service\SomeService"服务是私有的,您无法替换它。

好的,让我们尝试将其公开:

services.yaml:

AppServiceSomeService:
public: true
arguments:
$someBindedVariable: 200

但这无济于事。我从本地SomeService得到回复。让我们尝试使用别名:

some_service:
class: AppServiceSomeService
public: true
arguments:
$someBindedVariable: 200
AppServiceSomeService:
alias: some_service

同样,模拟对象不通过测试使用。我看到来自本机SomeService的响应。

我试图附加自动线选项,但没有帮助。

在测试期间,我应该怎么做才能将我的 SomeService 替换为整个项目中的一些模拟对象?

要模拟和测试服务,您可以放置以下配置:

config/packages/test/service.yaml

'test.myservice_mock':
class: AppServiceMyService
decorates: AppServiceMyService
factory: ['CodeceptionStub', makeEmpty]
arguments:
- '@test.myservice_mock.inner'

最好的方法是显式定义该服务并让它成为参数化的,以便您注入的类可以基于环境参数(因此,基本上,为您尝试注入的服务的测试和开发定义不同的实现(。

基本上,类似

<service id="yourService">
<argument type="service" id="%parametric.fully.qualified.class.name%" />
</service>

然后,您可以在common.yaml中定义一个用于实际实现的 FQCN

parameters:
parametric.fully.qualified.class.name: FullyQualifiedClassNameRealImpl

test.yaml(应该common.yaml加载以覆盖它(一个存根实现

parameters:
parametric.fully.qualified.class.name: FullyQualifiedClassNameStubImpl

您无需接触任何代码行或任何测试文件行即可完成:只需配置即可。

注意

这只是一个概念性和说明性示例,您应该将其调整为您的代码(例如,请参阅common.yamltest.yaml(和类名(请参阅yourService和所有 FQCN 内容(

此外,在symfony 4中,由于配置文件可以被.env文件替换以获得相同的结果,你只需要适应这些概念。

我对嘲笑也有同样的问题。但就我而言,我需要模拟ApiClient以避免在测试环境中进行真正的 API 调用,我需要使这个模拟可配置,以便能够实时模拟每个测试用例的任何请求/响应,或者添加几对请求/响应,因为在某些情况下需要对单个功能测试用例的不同端点进行多次 API 调用。以前的方式非常容易实现和阅读:

$apiClientMock = Mockery::mock(HttpClientInterface::class);
$apiClientMock
->shouldReceive('send')
->with($request)/            
->andReturn(new Response(HttpCode::OK, [], '{"data":"some data"}'))
->once();
$I->replaceSymfonyServiceWithMock($apiClientMock, HttpClientInterface::class);

因此,在上面的代码中,我可以为每个测试用例添加任意数量的实现。但是现在在Symfony中不起作用。我不知道为什么他们不能像他们在这里所做的那样为测试 env 制作一个容器 https://symfony.com/blog/new-in-symfony-4-1-simpler-service-testing 但也能够实时设置服务(我相信他们不能是有原因的,我对此不是很熟悉(。

我能做的是创建不同的服务实现并将其添加到 services_test.yml(此配置文件在测试时读取,因此您无需将类名作为 env 参数传递(,并在测试 env 中使用它,但我仍然无法在每个测试用例的基础上添加期望,我能做的只是在该实现中硬编码所有期望,但它对我来说是肮脏的解决方案, 创建模拟等简单任务变得非常头疼,因为您需要创建大量代码才能替换某些类功能,并且它使创建测试的过程变得复杂,但我认为这应该很简单

最新更新