如何在功能测试中使用symfony 4模拟服务? [英] How to mock service with symfony 4 in functional tests?

查看:76
本文介绍了如何在功能测试中使用symfony 4模拟服务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Commandbus处理程序,它注入了一些服务:

  class SomeHandler 
{
private $ service;

公共功能__construct(SomeService $ service)
{
$ this-> service = $ service;
}

公共测试(CommandTest $ command)
{
$ this-> service-> doSomeStuff();
}
}

SomeService有方法doSomeStuff与外部调用,我想要

  class SomeService 
{
private $ someBindedVariable;

公共功能__construct($ someBindedVariable)
{
$ this-> someBindedVariable = $ someBindedVariable;
}

公共函数doSomeStuff()
{
// TODO:一些东西
}
}

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

 公共函数testTest()
{
$ someService = $ this-> getMockBuilder(SomeService :: class)-> getMock();
$ this-> getContainer()-> set(SomeService :: class,$ someService);

// TODO:使用SomeHandler
}的路由功能测试

第一个问题是此代码将引发异常 App\Service\SomeService服务是私有的,不能替换。



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



services.yaml:

  App\Service\SomeService:
public:真
参数:
$ someBindedVariable:200

但这没有帮助。我从本地SomeService得到响应。让我们尝试使用别名:

  some_service:
类:App\Service\SomeService
public:真实的
参数:
$ someBindedVariable:200

App\Service\SomeService:
别名:some_service

同样,模拟对象也不被测试使用。我看到来自本地SomeService的响应。



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



在测试期间,我该怎么办?用整个项目中的某个模拟对象替换SomeService?

解决方案

最好的方法是明确定义该服务并使其具有参数性,以便您要注入的类可以基于环境参数(因此,基本上,为要注入的服务的测试和开发定义不同的实现)。 / p>

基本上,类似

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

然后您可以在 common.yaml 中定义真正实现的FQCN

 参数:
parametric.fully.qualified.class.name:完全合格\Class\Name\Real\Impl

并在测试中.yaml (应该在之后 common.yaml 加载,以便覆盖它)存根实现

 参数:
parametric.fully.qualified.class.name:Fully\Qualified\Class\Name\Stub mplImpl

您无需触摸任何代码行或测试文件行即可完成操作:



注意



这只是一个概念性和说明性的示例,您应该对其进行调整代码(例如,参见 common.yaml test.yaml )和类名(参见 yourService 和所有FQCN内容)



在symfony 4中,因为配置文件可以用 .env 文件替换以获得相同的结果,您只需要适应这些概念即可。


I have an commandbus handler, which injects some service:

class SomeHandler
{
    private $service;

    public function __construct(SomeService $service)
    {
        $this->service = $service;
    }

    public test(CommandTest $command)
    {
        $this->service->doSomeStuff();
    }
}

SomeService has method doSomeStuff with external calls, which I want not to use during testing.

class SomeService
{
    private $someBindedVariable;

    public function __construct($someBindedVariable)
    {
        $this->someBindedVariable = $someBindedVariable;
    }

    public function doSomeStuff()
    {
        //TODO: some stuff
    }
}

There is in the test I try to replace service with mock object

public function testTest()
{
    $someService = $this->getMockBuilder(SomeService::class)->getMock();
    $this->getContainer()->set(SomeService::class, $someService);

    //TODO: functional test for the route, which uses SomeHandler
}

The first problem is this code will throws exception "The "App\Service\SomeService" service is private, you cannot replace it."

Ok, let's try to make it public:

services.yaml:

App\Service\SomeService:
    public: true
    arguments:
        $someBindedVariable: 200

But it doesn't help. I get response from native SomeService. Let's try with aliases:

some_service:
    class: App\Service\SomeService
    public: true
    arguments:
        $someBindedVariable: 200

App\Service\SomeService:
    alias: some_service

And again the mock object does not use by test. I see response from native SomeService.

I tried to append autowire option, but it did not help.

What should I do to replace my SomeService with some mock object all over the project during test?

解决方案

Best way to do that is to defined explicitly that service and let it be parametric so the class you're injecting in could be based on the environment parameters (so, basically, define a different implementation for test and dev of the service you're trying to inject).

Basically, something like

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

Then you can define in common.yaml a FQCN for the real implementation

parameters:
    parametric.fully.qualified.class.name: Fully\Qualified\Class\Name\Real\Impl

and in test.yaml (that should be loaded after common.yaml in order to override it) a stub implementation

parameters:
    parametric.fully.qualified.class.name: Fully\Qualified\Class\Name\Stub\Impl

And you're done without touching any line of code or any line of test file: just configuration.

Pay attention

This is only a conceptual and illustrative example, you should adapt it to your code (see the common.yaml and test.yaml for instance) and to your class names (see the yourService and all the FQCN things)

Moreover in symfony 4 as config files can be replaced by the .env file in order to obtain the same result, you just need to adapt these concepts.

这篇关于如何在功能测试中使用symfony 4模拟服务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆