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

Time:09-22

I have a controller end-point that does an external API request under hood which I can't really make each time I run tests.

I'm using HttpClientInterface to make the request and now my idea is to replace it with MockHttpClient. So here is what I have so far:

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();
    }
}

But it gives me the following error:

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

which is kinda makes sense. Is there a better solution or how to overcome the problem?

CodePudding user response:

In a Symfony environment services are private, but this is not a problem because you are getting them in your controllers, services, etc through Dependency Injection, meaning that it is Symfony itself that takes care of it.

When trying to test, you may end up, like in your case, setting the mocked class in your container directly.

This will throw the error you see.

To overcome this error, in your services.yaml file located in the config folder, just add the following lines at the bottom:

when@test:
    services:
        _defaults:
            public: true

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

what you are doing here, is telling Symfony that during tests you will have a public Service called test.Symfony\Contracts\HttpClient\HttpClientInterface that it is a copy of the HTTPClientInterface class.

Now, in your test code you can do the following:

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