Symfony

How to mock the Symfony HttpClient

Hello, I will show you how we can mock the Symfony HttpClient.

Firstly we need to make a class which will collect all the needed information for our class.

<?php

namespace App\Component\Stateta;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

class Collector
{
    public function __construct(
        private readonly HttpClientInterface $client
    ) {
    }

    /**
     * @return Price[]|array
     *
     * @throws ClientExceptionInterface
     * @throws RedirectionExceptionInterface
     * @throws ServerExceptionInterface
     * @throws TransportExceptionInterface
     * @throws \Exception
     */
    public function collect(): array
    {
        $prices = [];

        $response = $this->client->request(
            'GET',
            $_ENV['STATETA_URL']
        );

        if (Response::HTTP_OK !== $response->getStatusCode()) {
            return $prices;
        }

        $matches = [];
        preg_match('/ProtocolData\ =(.*);/', $response->getContent(), $matches);
        if (0 === \count($matches)) {
            return $prices;
        }

        $statetaPrices = json_decode($matches[1], true);

        if (\count($matches) > 0) {
            $a95 = $statetaPrices[1];
            $dk = $statetaPrices[0];

            if ('A95' === $a95['key'] && 'DK' === $dk['key']) {
                foreach ($a95['values'] as $index => $priceA95) {
                    $price = new Price();
                    $price->setDate(new \DateTimeImmutable($priceA95['date']));
                    $price->setA95($priceA95['y']);
                    $price->setDk($dk['values'][$index]['y']);
                    $prices[] = $price;
                }
            }
        }

        return $prices;
    }
}

Secondly, we put a new test class for testing our mocked data.

<?php

namespace App\Tests\Component\Stateta;

use App\Component\Stateta\Collector;
use App\Component\Stateta\Price;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;

class CollectorTest extends TestCase
{
    /**
     * @dataProvider dataProviderCollect
     */
    public function testCollect(array $expected, array $provided)
    {
        $response = new MockResponse(
            [
                $provided['response'],
            ],
            [
                'http_code' => $provided['status_code'],
            ],
        );

        $client = new MockHttpClient($response);
        $collector = new Collector($client);

        self::assertEquals($expected, $collector->collect());
    }

    private function dataProviderCollect(): array
    {
        $price = new Price();
        $price->setA95('1.39');
        $price->setDk('1.227');
        $price->setDate(new \DateTimeImmutable('2023-06-10'));

        $price2 = new Price();
        $price2->setA95('1.39');
        $price2->setDk('1.227');
        $price2->setDate(new \DateTimeImmutable('2023-06-11'));

        $expected2 = [
            $price,
            $price2,
        ];

        $response = <<< RESPONSE
<script type="text/javascript">
var ProtocolData = [{"key":"DK","values":[{"x":1686344400000,"y":"1.227","date":"2023-06-10"},{"x":1686430800000,"y":"1.227","date":"2023-06-11"}]},{"key":"A95","values":[{"x":1686344400000,"y":"1.390","date":"2023-06-10"},{"x":1686430800000,"y":"1.390","date":"2023-06-11"}]}];
</script>
RESPONSE;

        return [
            [
                [],
                [
                    'status_code' => 200,
                    'response' => 'OK',
                ],
            ],
            [
                $expected2,
                [
                    'status_code' => 200,
                    'response' => $response,
                ],
            ],
            [
                [],
                [
                    'status_code' => 500,
                    'response' => 'ERROR',
                ],
            ],
        ];
    }
}