scratch – Blame information for rev 87
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
87 | office | 1 | ====================== |
2 | Testing Guzzle Clients |
||
3 | ====================== |
||
4 | |||
5 | Guzzle provides several tools that will enable you to easily mock the HTTP |
||
6 | layer without needing to send requests over the internet. |
||
7 | |||
8 | * Mock subscriber |
||
9 | * Mock adapter |
||
10 | * Node.js web server for integration testing |
||
11 | |||
12 | Mock Subscriber |
||
13 | =============== |
||
14 | |||
15 | When testing HTTP clients, you often need to simulate specific scenarios like |
||
16 | returning a successful response, returning an error, or returning specific |
||
17 | responses in a certain order. Because unit tests need to be predictable, easy |
||
18 | to bootstrap, and fast, hitting an actual remote API is a test smell. |
||
19 | |||
20 | Guzzle provides a mock subscriber that can be attached to clients or requests |
||
21 | that allows you to queue up a list of responses to use rather than hitting a |
||
22 | remote API. |
||
23 | |||
24 | .. code-block:: php |
||
25 | |||
26 | use GuzzleHttp\Client; |
||
27 | use GuzzleHttp\Subscriber\Mock; |
||
28 | use GuzzleHttp\Message\Response; |
||
29 | |||
30 | $client = new Client(); |
||
31 | |||
32 | // Create a mock subscriber and queue two responses. |
||
33 | $mock = new Mock([ |
||
34 | new Response(200, ['X-Foo' => 'Bar']), // Use response object |
||
35 | "HTTP/1.1 202 OK\r\nContent-Length: 0\r\n\r\n" // Use a response string |
||
36 | ]); |
||
37 | |||
38 | // Add the mock subscriber to the client. |
||
39 | $client->getEmitter()->attach($mock); |
||
40 | // The first request is intercepted with the first response. |
||
41 | echo $client->get('/')->getStatusCode(); |
||
42 | //> 200 |
||
43 | // The second request is intercepted with the second response. |
||
44 | echo $client->get('/')->getStatusCode(); |
||
45 | //> 202 |
||
46 | |||
47 | When no more responses are in the queue and a request is sent, an |
||
48 | ``OutOfBoundsException`` is thrown. |
||
49 | |||
50 | History Subscriber |
||
51 | ================== |
||
52 | |||
53 | When using things like the ``Mock`` subscriber, you often need to know if the |
||
54 | requests you expected to send were sent exactly as you intended. While the mock |
||
55 | subscriber responds with mocked responses, the ``GuzzleHttp\Subscriber\History`` |
||
56 | subscriber maintains a history of the requests that were sent by a client. |
||
57 | |||
58 | .. code-block:: php |
||
59 | |||
60 | use GuzzleHttp\Client; |
||
61 | use GuzzleHttp\Subscriber\History; |
||
62 | |||
63 | $client = new Client(); |
||
64 | $history = new History(); |
||
65 | |||
66 | // Add the history subscriber to the client. |
||
67 | $client->getEmitter()->attach($history); |
||
68 | |||
69 | $client->get('http://httpbin.org/get'); |
||
70 | $client->head('http://httpbin.org/get'); |
||
71 | |||
72 | // Count the number of transactions |
||
73 | echo count($history); |
||
74 | //> 2 |
||
75 | // Get the last request |
||
76 | $lastRequest = $history->getLastRequest(); |
||
77 | // Get the last response |
||
78 | $lastRequest = $history->getLastResponse(); |
||
79 | |||
80 | // Iterate over the transactions that were sent |
||
81 | foreach ($history as $transaction) { |
||
82 | echo $transaction['request']->getMethod(); |
||
83 | //> GET, HEAD |
||
84 | echo $transaction['response']->getStatusCode(); |
||
85 | //> 200, 200 |
||
86 | } |
||
87 | |||
88 | The history subscriber can also be printed, revealing the requests and |
||
89 | responses that were sent as a string, in order. |
||
90 | |||
91 | .. code-block:: php |
||
92 | |||
93 | echo $history; |
||
94 | |||
95 | :: |
||
96 | |||
97 | > GET /get HTTP/1.1 |
||
98 | Host: httpbin.org |
||
99 | User-Agent: Guzzle/4.0-dev curl/7.21.4 PHP/5.5.8 |
||
100 | |||
101 | < HTTP/1.1 200 OK |
||
102 | Access-Control-Allow-Origin: * |
||
103 | Content-Type: application/json |
||
104 | Date: Tue, 25 Mar 2014 03:53:27 GMT |
||
105 | Server: gunicorn/0.17.4 |
||
106 | Content-Length: 270 |
||
107 | Connection: keep-alive |
||
108 | |||
109 | { |
||
110 | "headers": { |
||
111 | "Connection": "close", |
||
112 | "X-Request-Id": "3d0f7d5c-c937-4394-8248-2b8e03fcccdb", |
||
113 | "User-Agent": "Guzzle/4.0-dev curl/7.21.4 PHP/5.5.8", |
||
114 | "Host": "httpbin.org" |
||
115 | }, |
||
116 | "origin": "76.104.247.1", |
||
117 | "args": {}, |
||
118 | "url": "http://httpbin.org/get" |
||
119 | } |
||
120 | |||
121 | > HEAD /get HTTP/1.1 |
||
122 | Host: httpbin.org |
||
123 | User-Agent: Guzzle/4.0-dev curl/7.21.4 PHP/5.5.8 |
||
124 | |||
125 | < HTTP/1.1 200 OK |
||
126 | Access-Control-Allow-Origin: * |
||
127 | Content-length: 270 |
||
128 | Content-Type: application/json |
||
129 | Date: Tue, 25 Mar 2014 03:53:27 GMT |
||
130 | Server: gunicorn/0.17.4 |
||
131 | Connection: keep-alive |
||
132 | |||
133 | Mock Adapter |
||
134 | ============ |
||
135 | |||
136 | In addition to using the Mock subscriber, you can use the |
||
137 | ``GuzzleHttp\Adapter\MockAdapter`` as the adapter of a client to return the |
||
138 | same response over and over or return the result of a callable function. |
||
139 | |||
140 | .. code-block:: php |
||
141 | |||
142 | use GuzzleHttp\Client; |
||
143 | use GuzzleHttp\Adapter\MockAdapter; |
||
144 | use GuzzleHttp\Adapter\TransactionInterface; |
||
145 | use GuzzleHttp\Message\Response; |
||
146 | |||
147 | $mockAdapter = new MockAdapter(function (TransactionInterface $trans) { |
||
148 | // You have access to the request |
||
149 | $request = $trans->getRequest(); |
||
150 | // Return a response |
||
151 | return new Response(200); |
||
152 | }); |
||
153 | |||
154 | $client = new Client(['adapter' => $mockAdapter]); |
||
155 | |||
156 | Test Web Server |
||
157 | =============== |
||
158 | |||
159 | Using mock responses is almost always enough when testing a web service client. |
||
160 | When implementing custom :doc:`HTTP adapters <adapters>`, you'll need to send |
||
161 | actual HTTP requests in order to sufficiently test the adapter. However, a |
||
162 | best practice is to contact a local web server rather than a server over the |
||
163 | internet. |
||
164 | |||
165 | - Tests are more reliable |
||
166 | - Tests do not require a network connection |
||
167 | - Tests have no external dependencies |
||
168 | |||
169 | Using the test server |
||
170 | --------------------- |
||
171 | |||
172 | .. warning:: |
||
173 | |||
174 | The following functionality is provided to help developers of Guzzle |
||
175 | develop HTTP adapters. There is no promise of backwards compatibility |
||
176 | when it comes to the node.js test server or the ``GuzzleHttp\Tests\Server`` |
||
177 | class. If you are using the test server or ``Server`` class outside of |
||
178 | guzzlehttp/guzzle, then you will need to configure autoloading and |
||
179 | ensure the web server is started manually. |
||
180 | |||
181 | .. hint:: |
||
182 | |||
183 | You almost never need to use this test web server. You should only ever |
||
184 | consider using it when developing HTTP adapters. The test web server |
||
185 | is not necessary for mocking requests. For that, please use the |
||
186 | Mock subcribers and History subscriber. |
||
187 | |||
188 | Guzzle ships with a node.js test server that receives requests and returns |
||
189 | responses from a queue. The test server exposes a simple API that is used to |
||
190 | enqueue responses and inspect the requests that it has received. |
||
191 | |||
192 | Any operation on the ``Server`` object will ensure that |
||
193 | the server is running and wait until it is able to receive requests before |
||
194 | returning. |
||
195 | |||
196 | .. code-block:: php |
||
197 | |||
198 | use GuzzleHttp\Client; |
||
199 | use GuzzleHttp\Tests\Server; |
||
200 | |||
201 | // Start the server and queue a response |
||
202 | Server::enqueue("HTTP/1.1 200 OK\r\n\Content-Length: 0r\n\r\n"); |
||
203 | |||
204 | $client = new Client(['base_url' => Server::$url]); |
||
205 | echo $client->get('/foo')->getStatusCode(); |
||
206 | // 200 |
||
207 | |||
208 | ``GuzzleHttp\Tests\Server`` provides a static interface to the test server. You |
||
209 | can queue an HTTP response or an array of responses by calling |
||
210 | ``Server::enqueue()``. This method accepts a string representing an HTTP |
||
211 | response message, a ``GuzzleHttp\Message\ResponseInterface``, or an array of |
||
212 | HTTP message strings / ``GuzzleHttp\Message\ResponseInterface`` objects. |
||
213 | |||
214 | .. code-block:: php |
||
215 | |||
216 | // Queue single response |
||
217 | Server::enqueue("HTTP/1.1 200 OK\r\n\Content-Length: 0r\n\r\n"); |
||
218 | |||
219 | // Clear the queue and queue an array of responses |
||
220 | Server::enqueue([ |
||
221 | "HTTP/1.1 200 OK\r\n\Content-Length: 0r\n\r\n", |
||
222 | "HTTP/1.1 404 Not Found\r\n\Content-Length: 0r\n\r\n" |
||
223 | ]); |
||
224 | |||
225 | When a response is queued on the test server, the test server will remove any |
||
226 | previously queued responses. As the server receives requests, queued responses |
||
227 | are dequeued and returned to the request. When the queue is empty, the server |
||
228 | will return a 500 response. |
||
229 | |||
230 | You can inspect the requests that the server has retrieved by calling |
||
231 | ``Server::received()``. This method accepts an optional ``$hydrate`` parameter |
||
232 | that specifies if you are retrieving an array of HTTP requests as strings or an |
||
233 | array of ``GuzzleHttp\Message\RequestInterface`` objects. |
||
234 | |||
235 | .. code-block:: php |
||
236 | |||
237 | foreach (Server::received() as $response) { |
||
238 | echo $response; |
||
239 | } |
||
240 | |||
241 | You can clear the list of received requests from the web server using the |
||
242 | ``Server::flush()`` method. |
||
243 | |||
244 | .. code-block:: php |
||
245 | |||
246 | Server::flush(); |
||
247 | echo count(Server::received()); |
||
248 | // 0 |