用PHPUnit重写Symfony私有服务进行功能测试的例子

105 阅读1分钟

在这个例子中,我们将覆盖Symfony的私有服务,并用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'

变化

如果我们要对上面的客户端类进行功能测试,我们会在运行测试时每次都调用external/api/endpoint ,这是我们不想看到的。为了防止这样的问题,我们要创建一个 "模拟 "版本,test 环境特定的services_test.yaml 文件,并在测试中使用它来代替。

# 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'

测试类

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