8000 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

11 files changed

+272
-0
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

+1
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`
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

+1
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

+4
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"
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+
}
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+
}
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+
];
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'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
_emailtest_bundle:
2+
resource: '@TestBundle/Resources/config/routing.yml'
Original file line numberDiff line numberDiff line change
@@ -0, 713F 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