testing.rst 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. ======================
  2. Testing Guzzle Clients
  3. ======================
  4. Guzzle provides several tools that will enable you to easily mock the HTTP
  5. layer without needing to send requests over the internet.
  6. * Mock handler
  7. * History middleware
  8. * Node.js web server for integration testing
  9. Mock Handler
  10. ============
  11. When testing HTTP clients, you often need to simulate specific scenarios like
  12. returning a successful response, returning an error, or returning specific
  13. responses in a certain order. Because unit tests need to be predictable, easy
  14. to bootstrap, and fast, hitting an actual remote API is a test smell.
  15. Guzzle provides a mock handler that can be used to fulfill HTTP requests with
  16. a response or exception by shifting return values off of a queue.
  17. .. code-block:: php
  18. use GuzzleHttp\Client;
  19. use GuzzleHttp\Handler\MockHandler;
  20. use GuzzleHttp\HandlerStack;
  21. use GuzzleHttp\Psr7\Response;
  22. use GuzzleHttp\Psr7\Request;
  23. use GuzzleHttp\Exception\RequestException;
  24. // Create a mock and queue two responses.
  25. $mock = new MockHandler([
  26. new Response(200, ['X-Foo' => 'Bar'], 'Hello, World'),
  27. new Response(202, ['Content-Length' => 0]),
  28. new RequestException('Error Communicating with Server', new Request('GET', 'test'))
  29. ]);
  30. $handlerStack = HandlerStack::create($mock);
  31. $client = new Client(['handler' => $handlerStack]);
  32. // The first request is intercepted with the first response.
  33. $response = $client->request('GET', '/');
  34. echo $response->getStatusCode();
  35. //> 200
  36. echo $response->getBody();
  37. //> Hello, World
  38. // The second request is intercepted with the second response.
  39. echo $client->request('GET', '/')->getStatusCode();
  40. //> 202
  41. // Reset the queue and queue up a new response
  42. $mock->reset();
  43. $mock->append(new Response(201));
  44. // As the mock was reset, the new response is the 201 CREATED,
  45. // instead of the previously queued RequestException
  46. echo $client->request('GET', '/')->getStatusCode();
  47. //> 201
  48. When no more responses are in the queue and a request is sent, an
  49. ``OutOfBoundsException`` is thrown.
  50. History Middleware
  51. ==================
  52. When using things like the ``Mock`` handler, you often need to know if the
  53. requests you expected to send were sent exactly as you intended. While the mock
  54. handler responds with mocked responses, the history middleware maintains a
  55. history of the requests that were sent by a client.
  56. .. code-block:: php
  57. use GuzzleHttp\Client;
  58. use GuzzleHttp\HandlerStack;
  59. use GuzzleHttp\Middleware;
  60. $container = [];
  61. $history = Middleware::history($container);
  62. $handlerStack = HandlerStack::create();
  63. // or $handlerStack = HandlerStack::create($mock); if using the Mock handler.
  64. // Add the history middleware to the handler stack.
  65. $handlerStack->push($history);
  66. $client = new Client(['handler' => $handlerStack]);
  67. $client->request('GET', 'http://httpbin.org/get');
  68. $client->request('HEAD', 'http://httpbin.org/get');
  69. // Count the number of transactions
  70. echo count($container);
  71. //> 2
  72. // Iterate over the requests and responses
  73. foreach ($container as $transaction) {
  74. echo $transaction['request']->getMethod();
  75. //> GET, HEAD
  76. if ($transaction['response']) {
  77. echo $transaction['response']->getStatusCode();
  78. //> 200, 200
  79. } elseif ($transaction['error']) {
  80. echo $transaction['error'];
  81. //> exception
  82. }
  83. var_dump($transaction['options']);
  84. //> dumps the request options of the sent request.
  85. }
  86. Test Web Server
  87. ===============
  88. Using mock responses is almost always enough when testing a web service client.
  89. When implementing custom :doc:`HTTP handlers <handlers-and-middleware>`, you'll
  90. need to send actual HTTP requests in order to sufficiently test the handler.
  91. However, a best practice is to contact a local web server rather than a server
  92. over the internet.
  93. - Tests are more reliable
  94. - Tests do not require a network connection
  95. - Tests have no external dependencies
  96. Using the test server
  97. ---------------------
  98. .. warning::
  99. The following functionality is provided to help developers of Guzzle
  100. develop HTTP handlers. There is no promise of backwards compatibility
  101. when it comes to the node.js test server or the ``GuzzleHttp\Tests\Server``
  102. class. If you are using the test server or ``Server`` class outside of
  103. guzzlehttp/guzzle, then you will need to configure autoloading and
  104. ensure the web server is started manually.
  105. .. hint::
  106. You almost never need to use this test web server. You should only ever
  107. consider using it when developing HTTP handlers. The test web server
  108. is not necessary for mocking requests. For that, please use the
  109. Mock handler and history middleware.
  110. Guzzle provides a node.js test server as a separate composer package that
  111. receives requests and returns responses from a queue. The test server exposes
  112. a simple API that is used to enqueue responses and inspect the requests that it
  113. has received.
  114. You can add the test server as a dev dependency using Composer.
  115. .. code-block:: bash
  116. composer require --dev guzzlehttp/test-server:^0.1
  117. Alternatively, you can include it as a dev dependency in your project's
  118. existing composer.json file:
  119. .. code-block:: json
  120. {
  121. "require-dev": {
  122. "guzzlehttp/test-server": "^0.1"
  123. }
  124. }
  125. Any operation on the ``Server`` object will ensure that
  126. the server is running and wait until it is able to receive requests before
  127. returning.
  128. ``GuzzleHttp\Tests\Server`` provides a static interface to the test server. You
  129. can queue an HTTP response or an array of responses by calling
  130. ``Server::enqueue()``. This method accepts an array of
  131. ``Psr\Http\Message\ResponseInterface`` and ``Exception`` objects.
  132. .. code-block:: php
  133. use GuzzleHttp\Client;
  134. use GuzzleHttp\Psr7\Response;
  135. use GuzzleHttp\Tests\Server;
  136. // Start the server and queue a response
  137. Server::enqueue([
  138. new Response(200, ['Content-Length' => 0])
  139. ]);
  140. $client = new Client(['base_uri' => Server::$url]);
  141. echo $client->request('GET', '/foo')->getStatusCode();
  142. // 200
  143. When a response is queued on the test server, the test server will remove any
  144. previously queued responses. As the server receives requests, queued responses
  145. are dequeued and returned to the request. When the queue is empty, the server
  146. will return a 500 response.
  147. You can inspect the requests that the server has retrieved by calling
  148. ``Server::received()``.
  149. .. code-block:: php
  150. foreach (Server::received() as $response) {
  151. echo $response->getStatusCode();
  152. }
  153. You can clear the list of received requests from the web server using the
  154. ``Server::flush()`` method.
  155. .. code-block:: php
  156. Server::flush();
  157. echo count(Server::received());
  158. // 0