@@ -1279,7 +1279,7 @@ Symfony's HTTP client provides an EventSource implementation to consume these
12791279server-sent events. Use the :class: `Symfony\\ Component\\ HttpClient\\ EventSourceHttpClient `
12801280to wrap your HTTP client, open a connection to a server that responds with a
12811281``text/event-stream `` content type and consume the stream as follows::
1282-
1282+
12831283 use Symfony\Component\HttpClient\Chunk\ServerSentEvent;
12841284 use Symfony\Component\HttpClient\EventSourceHttpClient;
12851285
@@ -1627,12 +1627,27 @@ has many safety checks that will throw a ``LogicException`` if the chunk
16271627passthru doesn't behave correctly; e.g. if a chunk is yielded after an ``isLast() ``
16281628one, or if a content chunk is yielded before an ``isFirst() `` one, etc.
16291629
1630- Testing HTTP Clients and Responses
1631- ----------------------------------
1630+ Testing
1631+ -------
16321632
16331633This component includes the ``MockHttpClient `` and ``MockResponse `` classes to
1634- use them in tests that need an HTTP client which doesn't make actual HTTP
1635- requests.
1634+ use in tests that shouldn't make actual HTTP requests. Such tests can be
1635+ useful, as they will run faster and produce consistent results, since they're
1636+ not dependent on an external service. By not making actual HTTP requests there
1637+ is no need to worry about the service being online or the request changing
1638+ state, for example deleting a resource.
1639+
1640+ ``MockHttpClient `` implements the ``HttpClientInterface ``, just like any actual
1641+ HTTP client in this component. When you type-hint with ``HttpClientInterface ``
1642+ your code will accept the real client outside tests, while replacing it with
1643+ ``MockHttpClient `` in the test.
1644+
1645+ When the ``request `` method is used on ``MockHttpClient ``, it will respond with
1646+ the supplied ``MockResponse ``. There are a few ways to use it, as described
1647+ below.
1648+
1649+ HTTP Client and Responses
1650+ ~~~~~~~~~~~~~~~~~~~~~~~~~
16361651
16371652The first way of using ``MockHttpClient `` is to pass a list of responses to its
16381653constructor. These will be yielded in order when requests are made::
@@ -1769,6 +1784,119 @@ Then configure Symfony to use your callback:
17691784 ;
17701785 };
17711786
1787+ Testing Request Data
1788+ ~~~~~~~~~~~~~~~~~~~~
1789+
1790+ The ``MockResponse `` class comes with some helper methods to test the request:
1791+
1792+ * ``getRequestMethod() `` - returns the HTTP method;
1793+ * ``getRequestUrl() `` - returns the URL the request would be sent to;
1794+ * ``getRequestOptions() `` - returns an array containing other information about
1795+ the request such as headers, query parameters, body content etc.
1796+
1797+ Usage example::
1798+
1799+ $mockResponse = new MockResponse('', ['http_code' => 204]);
1800+ $httpClient = new MockHttpClient($mockResponse, 'https://example.com');
1801+
1802+ $response = $httpClient->request('DELETE', 'api/article/1337', [
1803+ 'headers' => [
1804+ 'Accept: */*',
1805+ 'Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l',
1806+ ],
1807+ ]);
1808+
1809+ $mockResponse->getRequestMethod();
1810+ // returns "DELETE"
1811+
1812+ $mockResponse->getRequestUrl();
1813+ // returns "https://example.com/api/article/1337"
1814+
1815+ $mockResponse->getRequestOptions()['headers'];
1816+ // returns ["Accept: */*", "Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l"]
1817+
1818+ Full Example
1819+ ~~~~~~~~~~~~
1820+
1821+ The following standalone example demonstrates a way to use the HTTP client and
1822+ test it in a real application::
1823+
1824+ // ExternalArticleService.php
1825+ use Symfony\Contracts\HttpClient\HttpClientInterface;
1826+
1827+ final class ExternalArticleService
1828+ {
1829+ private HttpClientInterface $httpClient;
1830+
1831+ public function __construct(HttpClientInterface $httpClient)
1832+ {
1833+ $this->httpClient = $httpClient;
1834+ }
1835+
1836+ public function createArticle(array $requestData): array
1837+ {
1838+ $requestJson = json_encode($requestData, JSON_THROW_ON_ERROR);
1839+
1840+ $response = $this->httpClient->request('POST', 'api/article', [
1841+ 'headers' => [
1842+ 'Content-Type: application/json',
1843+ 'Accept: application/json',
1844+ ],
1845+ 'body' => $requestJson,
1846+ ]);
1847+
1848+ if (201 !== $response->getStatusCode()) {
1849+ throw new Exception('Response status code is different than expected.');
1850+ }
1851+
1852+ // ... other checks
1853+
1854+ $responseJson = $response->getContent();
1855+ $responseData = json_decode($responseJson, true, 512, JSON_THROW_ON_ERROR);
1856+
1857+ return $responseData;
1858+ }
1859+ }
1860+
1861+ // ExternalArticleServiceTest.php
1862+ use PHPUnit\Framework\TestCase;
1863+ use Symfony\Component\HttpClient\MockHttpClient;
1864+ use Symfony\Component\HttpClient\Response\MockResponse;
1865+
1866+ final class ExternalArticleServiceTest extends TestCase
1867+ {
1868+ public function testSubmitData(): void
1869+ {
1870+ // Arrange
1871+ $requestData = ['title' => 'Testing with Symfony HTTP Client'];
1872+ $expectedRequestData = json_encode($requestData, JSON_THROW_ON_ERROR);
1873+
1874+ $expectedResponseData = ['id' => 12345];
1875+ $mockResponseJson = json_encode($expectedResponseData, JSON_THROW_ON_ERROR);
1876+ $mockResponse = new MockResponse($mockResponseJson, [
1877+ 'http_code' => 201,
1878+ 'response_headers' => ['Content-Type: application/json'],
1879+ ]);
1880+
1881+ $httpClient = new MockHttpClient($mockResponse, 'https://example.com');
1882+ $service = new ExternalArticleService($httpClient);
1883+
1884+ // Act
1885+ $responseData = $service->createArticle($requestData);
1886+
1887+ // Assert
1888+ self::assertSame('POST', $mockResponse->getRequestMethod());
1889+ self::assertSame('https://example.com/api/article', $mockResponse->getRequestUrl());
1890+ self::assertContains(
1891+ 'Content-Type: application/json',
1892+ $mockResponse->getRequestOptions()['headers']
1893+ );
1894+ self::assertSame($expectedRequestData, $mockResponse->getRequestOptions()['body']);
1895+
1896+ self::assertSame($responseData, $expectedResponseData);
1897+ }
1898+ }
1899+
17721900.. _`cURL PHP extension` : https://www.php.net/curl
17731901.. _`PSR-17` : https://www.php-fig.org/psr/psr-17/
17741902.. _`PSR-18` : https://www.php-fig.org/psr/psr-18/
0 commit comments