In this example we are going to override Symfony private services and functional test them with PHPUnit.


HttpClient


namespace App\Util;

interface HttpClientInterface
{
public function isTokenValid(string $token): bool;
}

namespace App\Util;

use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;

class HttpClient implements HttpClientInterface
{
private $client;

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

public function isTokenValid(string $token): bool
{
/** @var Response $response */
$response = $this->client->request(
'GET',
'external/api/endpoint',
['headers' => ['Authorization' => $token]]
);

return 'true' === $response->getBody()->getContents();
}
}

# config/services.yaml
services:
_defaults:
autowire: true
autoconfigure: true
public: false

App\Util\HttpClient:
arguments:
- '@GuzzleHttp\Client'

Changes


If we are to use a functional test for the client class above, we would end up calling external/api/endpoint everytime when we run the tests which is something we don't want. To prevent such issue, we want to create a "mock" version of it, test environment specific services_test.yaml file and use it in out tests instead.


# tests/Functional/Mock/Util/HttpClientMock.php

namespace Tests\Functional\Mock\Util;

use App\Util\HttpClientInterface;

class HttpClientMock implements HttpClientInterface
{
public function isTokenValid(string $token): bool
{
return $token === 'Bearer valid-token';
}
}

# config/packages/test/services_test.yaml
services:
_defaults:
public: true

test.App\Util\HttpClient: '@App\Util\HttpClient'

Test class


declare(strict_types=1);

namespace Tests\Functional\Controller;

use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Tests\Functional\Mock\Util\HttpClientMock;

class YourControllerTest extends WebTestCase
{
/**
* @test
*/
public function return_unauthorised_response_if_the_token_is_not_valid(): void
{
$client = static::createClient();

$client->getContainer()->set('test.App\Util\HttpClient', new HttpClientMock());

$client->request(Request::METHOD_GET, '/', [], [], ['HTTPS' => true, 'HTTP_authorization' => 'Bearer invalid-user-token']);

self::assertSame(Response::HTTP_UNAUTHORIZED, $client->getResponse()->getStatusCode());
}

/**
* @test
*/
public function return_authorised_response_if_the_token_is_valid(): void
{
$client = static::createClient();

$client->getContainer()->set('test.App\Util\HttpClient', new HttpClientMock());

$client->request(Request::METHOD_GET, '/', [], [], ['HTTPS' => true, 'HTTP_authorization' => 'Bearer valid-user-token']);

self::assertSame(Response::HTTP_OK, $client->getResponse()->getStatusCode());
}
}