Symfony 5:如何在集成测试中模拟 HttpClientInterface?

Symfony 5: how do I mock HttpClientInterface in integration test?

我有一个控制器端点,它在幕后执行外部 API 请求,我无法在每次 运行 测试时真正做到这一点。

我正在使用 HttpClientInterface 发出请求,现在我的想法是将其替换为 MockHttpClient。所以这是我到目前为止所拥有的:

class MyControllerTest extends WebTestCase
{
    public function testSomething(): void
    {
        $client = static::createClient();

        $response = new MockResponse(null, ['http_code' => 200]);
        $client->getContainer()->set(HttpClientInterface::class, new MockHttpClient($response));

        $client->request('GET', '/my-end-point');

        self::assertResponseIsSuccessful();
    }
}

但是它给我以下错误:

The "Symfony\Contracts\HttpClient\HttpClientInterface" service is private, you cannot replace it

这有点道理。是否有更好的解决方案或如何克服问题?

在 Symfony 环境中,服务是私有的,但这不是问题,因为您是通过依赖注入在控制器、服务等中获取它们的,这意味着它是 Symfony 本身来处理的。

尝试测试时,您最终可能会像您的情况一样,直接在您的容器中设置模拟 class。

这将抛出您看到的错误。

要解决此错误,请在配置文件夹中的 services.yaml 文件中,在底部添加以下行:

when@test:
    services:
        _defaults:
            public: true

        test.Symfony\Contracts\HttpClient\HttpClientInterface: '@Symfony\Contracts\HttpClient\HttpClientInterface'

你在这里做的是告诉 Symfony 在测试期间你将有一个名为 test 的 public 服务。Symfony\Contracts\HttpClient\HttpClientInterface 它是 HTTPClientInterface class 的副本。

现在,在您的测试代码中,您可以执行以下操作:

$response = new MockResponse(null, ['http_code' => 200]);
$client->getContainer()->set('test.Symfony\Contracts\HttpClient\HttpClientInterface', new TraceableHttpClient(new MockHttpClient($response)));

您可以在 service.yaml 旁边创建一个 service_test.yaml 文件并在其中放入以下代码:

services:
    _defaults:
        public: true

    Symfony\Contracts\HttpClient\HttpClientInterface: ~

它使服务 public 用于测试环境并可以对其进行模拟。