10000 Add HttpClientAssertionsTrait which provide shortcut to assert HTTP c… · symfony/symfony@92bea53 · GitHub
[go: up one dir, main page]

Skip to content

Commit 92bea53

Browse files
committed
Add HttpClientAssertionsTrait which provide shortcut to assert HTTP call was triggered
1 parent 3cc427d commit 92bea53

File tree

11 files changed

+272
-0
lines changed
  • src/Symfony/Bundle/FrameworkBundle
  • app/HttpClient
  • Test
  • 11 files changed

    +272
    -0
    lines changed

    src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

    Lines changed: 1 addition & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -4,6 +4,7 @@ CHANGELOG
    44
    6.4
    55
    ---
    66

    7+
    * Add `HttpClientAssertionsTrait`
    78
    * Add `AbstractController::renderBlock()` and `renderBlockView()`
    89
    * Add native return type to `Translator` and to `Application::reset()`
    910
    * Deprecate the integration of Doctrine annotations, either uninstall the `doctrine/annotations` package or disable the integration by setting `framework.annotations` to `false`
    Lines changed: 134 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,134 @@
    1+
    <?php
    2+
    3+
    /*
    4+
    * This file is part of the Symfony package.
    5+
    *
    6+
    * (c) Fabien Potencier <fabien@symfony.com>
    7+
    *
    8+
    * For the full copyright and license information, please view the LICENSE
    9+
    * file that was distributed with this source code.
    10+
    */
    11+
    12+
    namespace Symfony\Bundle\FrameworkBundle\Test;
    13+
    14+
    use Symfony\Bundle\FrameworkBundle\KernelBrowser;
    15+
    use Symfony\Component\HttpClient\DataCollector\HttpClientDataCollector;
    16+
    17+
    /*
    18+
    * @author Mathieu Santostefano <msantostefano@protonmail.com>
    19+
    */
    20+
    21+
    trait HttpClientAssertionsTrait
    22+
    {
    23+
    public static function assertHttpClientRequest(string $expectedUrl, string $expectedMethod = 'GET', string|array $expectedBody = null, array $expectedHeaders = [], string $httpClientId = 'http_client'): void
    24+
    {
    25+
    /** @var KernelBrowser $client */
    26+
    $client = static::getClient();
    27+
    28+
    if (!($profile = $client->getProfile())) {
    29+
    static::fail('The Profiler must be enabled for the current request. Please ensure you have called $client->enableProfiler() before making the request.');
    30+
    }
    31+
    32+
    /** @var HttpClientDataCollector $httpClientDataCollector */
    33+
    $httpClientDataCollector = $profile->getCollector('http_client');
    34+
    $expectedRequestHasBeenFound = false;
    35+
    36+
    if (!\array_key_exists($httpClientId, $httpClientDataCollector->getClients())) {
    37+
    static::fail(sprintf('The HttpClient "%s" is not registered.', $httpClientId));
    38+
    }
    39+
    40+
    foreach ($httpClientDataCollector->getClients()[$httpClientId]['traces'] as $trace) {
    41+
    if (($expectedUrl !== $trace['info']['url'] && $expectedUrl !== $trace['url'])
    42+
    || $expectedMethod !== $trace['method']
    43+
    ) {
    44+
    continue;
    45+
    }
    46+
    47+
    if (null !== $expectedBody) {
    48+
    $actualBody = null;
    49+
    50+
    if (null !== $trace['options']['body'] && null === $trace['options']['json']) {
    51+
    $actualBody = \is_string($trace['options']['body']) ? $trace['options']['body'] : $trace['options']['body']->getValue(true);
    52+
    }
    53+
    54+
    if (null === $trace['options']['body'] && null !== $trace['options']['json']) {
    55+
    $actualBody = $trace['options']['json']->getValue(true);
    56+
    }
    57+
    58+
    if (!$actualBody) {
    59+
    continue;
    60+
    }
    61+
    62+
    if ($expectedBody === $actualBody) {
    63+
    $expectedRequestHasBeenFound = true;
    64+
    65+
    if ([] === $expectedHeaders) {
    66+
    break;
    67+
    }
    68+
    }
    69+
    }
    70+
    71+
    if ([] !== $expectedHeaders) {
    72+
    $actualHeaders = $trace['options']['headers'] ?? [];
    73+
    74+
    foreach ($actualHeaders as $headerKey => $actualHeader) {
    75+
    if (\array_key_exists($headerKey, $expectedHeaders)
    76+
    && $expectedHeaders[$headerKey] === $actualHeader->getValue(true)
    77+
    ) {
    78+
    $expectedRequestHasBeenFound = true;
    79+
    break 2;
    80+
    }
    81+
    }
    82+
    }
    83+
    84+
    $expectedRequestHasBeenFound = true;
    85+
    break;
    86+
    }
    87+
    88+
    self::assertTrue($expectedRequestHasBeenFound, 'The expected request has not been called: "'.$expectedMethod.'" - "'.$expectedUrl.'"');
    89+
    }
    90+
    91+
    public function assertNotHttpClientRequest(string $unexpectedUrl, string $expectedMethod = 'GET', string $httpClientId = 'http_client'): void
    92+
    {
    93+
    /** @var KernelBrowser $client */
    94+
    $client = static::getClient();
    95+
    96+
    if (!($profile = $client->getProfile())) {
    97+
    static::fail('The Profiler must be enabled for the current request. Please ensure you have called $client->enableProfiler() before making the request.');
    98+
    }
    99+
    100+
    /** @var HttpClientDataCollector $httpClientDataCollector */
    101+
    $httpClientDataCollector = $profile->getCollector('http_client');
    102+
    $unexpectedUrlHasBeenFound = false;
    103+
    104+
    if (!\array_key_exists($httpClientId, $httpClientDataCollector->getClients())) {
    105+
    static::fail(sprintf('The HttpClient "%s" is not registered.', $httpClientId));
    106+
    }
    107+
    108+
    foreach ($httpClientDataCollector->getClients()[$httpClientId]['traces'] as $trace) {
    109+
    if (($unexpectedUrl === $trace['info']['url'] || $unexpectedUrl === $trace['url'])
    110+
    && $expectedMethod === $trace['method']
    111+
    ) {
    112+
    $unexpectedUrlHasBeenFound = true;
    113+
    break;
    114+
    }
    115+
    }
    116+
    117+
    self::assertFalse($unexpectedUrlHasBeenFound, 'The unexpected URL has been called: "'.$expectedMethod.'" - "'.$unexpectedUrl.'"');
    118+
    }
    119+
    120+
    public static function assertHttpClientRequestCount(int $count, string $httpClientId): void
    121+
    {
    122+
    /** @var KernelBrowser $client */
    123+
    $client = static::getClient();
    124+
    125+
    if (!($profile = $client->getProfile())) {
    126+
    static::fail('The Profiler must be enabled for the current request. Please ensure you have called $client->enableProfiler() before making the request.');
    127+
    }
    128+
    129+
    /** @var HttpClientDataCollector $httpClientDataCollector */
    130+
    $httpClientDataCollector = $profile->getCollector('http_client');
    131+
    132+
    self::assertCount($count, $httpClientDataCollector->getClients()[$httpClientId]['traces']);
    133+
    }
    134+
    }

    src/Symfony/Bundle/FrameworkBundle/Test/WebTestAssertionsTrait.php

    Lines changed: 1 addition & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -15,4 +15,5 @@ trait WebTestAssertionsTrait
    1515
    {
    1616
    use BrowserKitAssertionsTrait;
    1717
    use DomCrawlerAssertionsTrait;
    18+
    use HttpClientAssertionsTrait;
    1819
    }
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,35 @@
    1+
    <?php
    2+
    3+
    /*
    4+
    * This file is part of the Symfony package.
    5+
    *
    6+
    * (c) Fabien Potencier <fabien@symfony.com>
    7+
    *
    8+
    * For the full copyright and license information, please view the LICENSE
    9+
    * file that was distributed with this source code.
    10+
    */
    11+
    12+
    namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller;
    13+
    14+
    use Symfony\Component\HttpFoundation\Response;
    15+
    use Symfony\Contracts\HttpClient\HttpClientInterface;
    16+
    17+
    class HttpClientController
    18+
    {
    19+
    public function index(HttpClientInterface $httpClient, HttpClientInterface $symfonyHttpClient): Response
    20+
    {
    21+
    $httpClient->request('GET', 'https://symfony.com/');
    22+
    23+
    $symfonyHttpClient->request('GET', '/');
    24+
    $symfonyHttpClient->request('POST', '/', ['body' => 'foo']);
    25+
    $symfonyHttpClient->request('POST', '/', ['body' => ['foo' => 'bar']]);
    26+
    $symfonyHttpClient->request('POST', '/', ['json' => ['foo' => 'bar']]);
    27+
    $symfonyHttpClient->request('POST', '/', [
    28+
    'headers' => ['X-Test-Header' => 'foo'],
    29+
    'json' => ['foo' => 'bar'],
    30+
    ]);
    31+
    $symfonyHttpClient->request('GET', '/doc/current/index.html');
    32+
    33+
    return new Response();
    34+
    }
    35+
    }

    src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml

    Lines changed: 4 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -61,6 +61,10 @@ send_email:
    6161
    path: /send_email
    6262
    defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\EmailController::indexAction }
    6363

    64+
    http_client_call:
    65+
    path: /http_client_call
    66+
    defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\HttpClientController::index }
    67+
    6468
    uid:
    6569
    resource: "../../Controller/UidController.php"
    6670
    type: "annotation"
    Lines changed: 23 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,23 @@
    1+
    <?php
    2+
    3+
    /*
    4+
    * This file is part of the Symfony package.
    5+
    *
    6+
    * (c) Fabien Potencier <fabien@symfony.com>
    7+
    *
    8+
    * For the full copyright and license information, please view the LICENSE
    9+
    * file that was distributed with this source code.
    10+
    */
    11+
    12+
    namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Tests;
    13+
    14+
    use Symfony\Component\HttpClient\Response\MockResponse;
    15+
    use Symfony\Contracts\HttpClient\ResponseInterface;
    16+
    17+
    class MockClientCallback
    18+
    {
    19+
    public function __invoke(string $method, string $url, array $options = []): ResponseInterface
    20+
    {
    21+
    return new MockResponse('foo');
    22+
    }
    23+
    }
    Lines changed: 33 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,33 @@
    1+
    <?php
    2+
    3+
    /*
    4+
    * This file is part of the Symfony package.
    5+
    *
    6+
    * (c) Fabien Potencier <fabien@symfony.com>
    7+
    *
    8+
    * For the full copyright and license information, please view the LICENSE
    9+
    * file that was distributed with this source code.
    10+
    */
    11+
    12+
    namespace Symfony\Bundle\FrameworkBundle\Tests\Functional;
    13+
    14+
    class HttpClientTest extends AbstractWebTestCase
    15+
    {
    16+
    public function testHttpClientAssertions()
    17+
    {
    18+
    $client = $this->createClient(['test_case' => 'HttpClient', 'root_config' => 'config.yml', 'debug' => true]);
    19+
    $client->enableProfiler();
    20+
    $client->request('GET', '/http_client_call');
    21+
    22+
    $this->assertHttpClientRequest('https://symfony.com/');
    23+
    $this->assertHttpClientRequest('https://symfony.com/', httpClientId: 'symfony.http_client');
    24+
    $this->assertHttpClientRequest('https://symfony.com/', 'POST', 'foo', httpClientId: 'symfony.http_client');
    25+
    $this->assertHttpClientRequest('https://symfony.com/', 'POST', ['foo' => 'bar'], httpClientId: 'symfony.http_client');
    26+
    $this->assertHttpClientRequest('https://symfony.com/', 'POST', ['foo' => 'bar'], httpClientId: 'symfony.http_client');
    27+
    $this->assertHttpClientRequest('https://symfony.com/', 'POST', ['foo' => 'bar'], ['X-Test-Header' => 'foo'], 'symfony.http_client');
    28+
    $this->assertHttpClientRequest('https://symfony.com/doc/current/index.html', httpClientId: 'symfony.http_client');
    29+
    $this->assertNotHttpClientRequest('https://laravel.com', httpClientId: 'symfony.http_client');
    30+
    31+
    $this->assertHttpClientRequestCount(6, 'symfony.http_client');
    32+
    }
    33+
    }
    Lines changed: 18 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,18 @@
    1+
    <?php
    2+
    3+
    /*
    4+
    * This file is part of the Symfony package.
    5+
    *
    6+
    * (c) Fabien Potencier <fabien@symfony.com>
    7+
    *
    8+
    * For the full copyright and license information, please view the LICENSE
    9+
    * file that was distributed with this source code.
    10+
    */
    11+
    12+
    use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
    13+
    use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle;
    14+
    15+
    return [
    16+
    new FrameworkBundle(),
    17+
    new TestBundle(),
    18+
    ];
    Lines changed: 12 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,12 @@
    1+
    imports:
    2+
    - { resource: ../config/default.yml }
    3+
    - { resource: services.yml }
    4+
    5+
    framework:
    6+
    http_method_override: false
    7+
    profiler: ~
    8+
    http_client:
    9+
    mock_response_factory: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Tests\MockClientCallback
    10+
    scoped_clients:
    11+
    symfony.http_client:
    12+
    base_uri: 'https://symfony.com'
    Lines changed: 2 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,2 @@
    1+
    _emailtest_bundle:
    2+
    resource: '@TestBundle/Resources/config/routing.yml'
    Lines changed: 9 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,9 @@
    1+
    services:
    2+
    _defaults:
    3+
    public: true
    4+
    5+
    Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\HttpClientController:
    6+
    tags: ['controller.service_arguments']
    7+
    8+
    Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Tests\MockClientCallback:
    9+
    class: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Tests\MockClientCallback

    0 commit comments

    Comments
     (0)
    0