Symfony 测试如何通过容器传递依赖项给服务

huangapple go评论65阅读模式
英文:

Symfony tests how to pass depencencies to services via container

问题

我正在为一个Symfony 4.4应用编写测试,我需要模拟一个名为TokenService的服务,它是我需要测试的一些类(repositories、services)的依赖项,但我不确定如何通过DI传递一些依赖项:

self::$container->get('App\Services\classToTest') // 无法传递依赖项?

我是否被迫实例化该类并执行类似以下操作:

$classToTest = new \App\Services\classToTest($depencencyOne, $tokenMock, ...)

此外,我只需要模拟其中一个依赖项,所以我是否仍然必须传递所有其他依赖项?

英文:

I'm writing tests for a Symfony 4.4 app and I need to mock a service named TokenService which is a dependency of some classes that I need to test (repositories, services), but I'm not sure how to proceed to pass some dependencies via DI :

 self::$container->get('App\Services\classToTest') // Not able to pass dependencies ?

Am I forced to instantiate the class and do something like this :

$classToTest = new \App\Services\classToTest($depencencyOne,$tokenMock,...)

Also, I need to mock only one of the dependencies, so do I have to pass all the other dependencies anyway ?

答案1

得分: 1

你可以从测试服务容器中使用get(),但也可以使用set()

我可以提供一个最近遇到的真实示例,其中我需要在某些测试中替换HttpClient为其模拟实现:

// 在这里,我用模拟实现替换了服务(PHPUnit模拟也可以使用)
self::getContainer()->set(HttpClientInterface::class, new MockHttpClient([
    new MockResponse('{"some":"JSON"}'),
    new MockResponse('{"other":"JSON"}'),
]));

// 这里$myService将获得模拟注入,而不是原始的HTTP客户端
$myService = self::getContainer()->get(MyService::class);

但需要注意,我必须将服务设置为公共的才能允许覆盖它:

# services.yaml
when@test:
    services:
        Symfony\Contracts\HttpClient\HttpClientInterface:
            alias: '.debug.http_client'
            public: true

如果您不能或不想替换服务容器中的依赖项,那么是的,您将需要自己实例化被测试的服务并获取每个依赖项:

$myService = new MyService(
     self::getContainer()->get(RealService::class),
     $someMockDependency
);

如果问题源自您自己的代码,请确保遵循良好的实践,以使您的代码更容易进行测试(主要是依赖注入依赖反转原则)。

英文:

You can get() from the testing service container but you can also set() !

I can give a real example I recently had where I needed to replace the HttpClient with its mock implementation in some tests:

// Here I replace the service with a mock implementation (a PHPUnit mock can work too)
self::getContainer()->set(HttpClientInterface::class, new MockHttpClient([
    new MockResponse('{"some":"JSON"}'),
    new MockResponse('{"other":"JSON"}'),
]));

// Here $myService will get the mock injected instead of the original HTTP Client
$myService = self::getContainer()->get(MyService::class);

But it should be noted that I had to make the service public to be allowed to overwrite it:

# services.yaml
when@test:
    services:
        Symfony\Contracts\HttpClient\HttpClientInterface:
            alias: '.debug.http_client'
            public: true

If you don't (or can't) replace a dependency in the service container, then yes you will need to instantiate the tested service yourself and get every dependency:

$myService = new MyService(
     self::getContainer()->get(RealService::class),
     $someMockDependency
);

If the issue comes from your own code, be sure to follow good practices to make your code easier to test (mostly dependency injection and dependency inversion principles).

huangapple
  • 本文由 发表于 2023年6月13日 16:12:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76462910.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定