UNPKG

10.9 kBMarkdownView Raw
1# Chai HTTP [![Build Status](https://travis-ci.org/chaijs/chai-http.svg?branch=master)](https://travis-ci.org/chaijs/chai-http)
2
3> HTTP integration testing with Chai assertions.
4
5#### Features
6
7- integration test request composition
8- test http apps or external services
9- assertions for common http tasks
10- chai `expect` and `should` interfaces
11
12#### Installation
13
14This is a addon plugin for the [Chai Assertion Library](http://chaijs.com). Install via [npm](http://npmjs.org).
15
16 npm install chai-http
17
18#### Plugin
19
20Use this plugin as you would all other Chai plugins.
21
22```js
23var chai = require('chai')
24 , chaiHttp = require('chai-http');
25
26chai.use(chaiHttp);
27```
28
29To use Chai HTTP on a web page, just include the [`dist/chai-http.js`](dist/chai-http.js) file:
30
31```html
32<script src="chai.js"></script>
33<script src="chai-http.js"></script>
34<script>
35 chai.use(chaiHttp);
36</script>
37```
38
39## Integration Testing
40
41Chai HTTP provides an interface for live integration
42testing via [superagent](https://github.com/visionmedia/superagent).
43To do this, you must first
44construct a request to an application or url.
45
46Upon construction you are provided a chainable api that
47allows you to specify the http VERB request (get, post, etc)
48that you wish to invoke.
49
50#### Application / Server
51
52You may use a function (such as an express or connect app)
53or a node.js http(s) server as the foundation for your request.
54If the server is not running, chai-http will find a suitable
55port to listen on for a given test.
56
57__Note:__ This feature is only supported on Node.js, not in web browsers.
58
59```js
60chai.request(app)
61 .get('/')
62```
63
64When passing an `app` to `request`; it will automatically open the server for
65incoming requests (by calling `listen()`) and, once a request has been made
66the server will automatically shut down (by calling `.close()`). If you want to
67keep the server open, perhaps if you're making multiple requests, you must call
68`.keepOpen()` after `.request()`, and manually close the server down:
69
70```js
71var requester = chai.request(app).keepOpen()
72
73Promise.all([
74 requester.get('/a'),
75 requester.get('/b'),
76])
77.then(responses => ....)
78.then(() => requester.close())
79```
80
81
82#### URL
83
84You may also use a base url as the foundation of your request.
85
86```js
87chai.request('http://localhost:8080')
88 .get('/')
89```
90
91#### Setting up requests
92
93Once a request is created with a given VERB, it can have headers, form data,
94json, or even file attachments added to it, all with a simple API:
95
96```js
97// Send some JSON
98chai.request(app)
99 .put('/user/me')
100 .set('X-API-Key', 'foobar')
101 .send({ password: '123', confirmPassword: '123' })
102```
103
104```js
105// Send some Form Data
106chai.request(app)
107 .post('/user/me')
108 .type('form')
109 .send({
110 '_method': 'put',
111 'password': '123',
112 'confirmPassword': '123'
113 })
114```
115
116```js
117// Attach a file
118chai.request(app)
119 .post('/user/avatar')
120 .attach('imageField', fs.readFileSync('avatar.png'), 'avatar.png')
121```
122
123```js
124// Authenticate with Basic authentication
125chai.request(app)
126 .get('/protected')
127 .auth('user', 'pass')
128```
129
130```js
131// Chain some GET query parameters
132chai.request(app)
133 .get('/search')
134 .query({name: 'foo', limit: 10}) // /search?name=foo&limit=10
135```
136
137#### Dealing with the response - traditional
138
139In the following examples we use Chai's Expect assertion library:
140
141```js
142var expect = chai.expect;
143```
144
145To make the request and assert on its response, the `end` method can be used:
146
147```js
148chai.request(app)
149 .put('/user/me')
150 .send({ password: '123', confirmPassword: '123' })
151 .end(function (err, res) {
152 expect(err).to.be.null;
153 expect(res).to.have.status(200);
154 });
155```
156
157##### Caveat
158
159Because the `end` function is passed a callback, assertions are run
160asynchronously. Therefore, a mechanism must be used to notify the testing
161framework that the callback has completed. Otherwise, the test will pass before
162the assertions are checked.
163
164For example, in the [Mocha test framework](http://mochajs.org/), this is
165accomplished using the
166[`done` callback](https://mochajs.org/#asynchronous-code), which signal that the
167callback has completed, and the assertions can be verified:
168
169```js
170it('fails, as expected', function(done) { // <= Pass in done callback
171 chai.request('http://localhost:8080')
172 .get('/')
173 .end(function(err, res) {
174 expect(res).to.have.status(123);
175 done(); // <= Call done to signal callback end
176 });
177});
178
179it('succeeds silently!', function() { // <= No done callback
180 chai.request('http://localhost:8080')
181 .get('/')
182 .end(function(err, res) {
183 expect(res).to.have.status(123); // <= Test completes before this runs
184 });
185});
186```
187
188When `done` is passed in, Mocha will wait until the call to `done()`, or until
189the [timeout](http://mochajs.org/#timeouts) expires. `done` also accepts an
190error parameter when signaling completion.
191
192#### Dealing with the response - Promises
193
194If `Promise` is available, `request()` becomes a Promise capable library -
195and chaining of `then`s becomes possible:
196
197```js
198chai.request(app)
199 .put('/user/me')
200 .send({ password: '123', confirmPassword: '123' })
201 .then(function (res) {
202 expect(res).to.have.status(200);
203 })
204 .catch(function (err) {
205 throw err;
206 });
207```
208
209__Note:__ Node.js version 0.10.x and some older web browsers do not have
210native promise support. You can use any spec compliant library, such as:
211 - [kriskowal/q](https://github.com/kriskowal/q)
212 - [stefanpenner/es6-promise](https://github.com/stefanpenner/es6-promise)
213 - [petkaantonov/bluebird](https://github.com/petkaantonov/bluebird)
214 - [then/promise](https://github.com/then/promise)
215You will need to set the library you use to `global.Promise`, before
216requiring in chai-http. For example:
217
218```js
219// Add promise support if this does not exist natively.
220if (!global.Promise) {
221 global.Promise = require('q');
222}
223var chai = require('chai');
224chai.use(require('chai-http'));
225```
226
227#### Retaining cookies with each request
228
229Sometimes you need to keep cookies from one request, and send them with the
230next (for example, when you want to login with the first request, then access an authenticated-only resource later). For this, `.request.agent()` is available:
231
232```js
233// Log in
234var agent = chai.request.agent(app)
235agent
236 .post('/session')
237 .send({ username: 'me', password: '123' })
238 .then(function (res) {
239 expect(res).to.have.cookie('sessionid');
240 // The `agent` now has the sessionid cookie saved, and will send it
241 // back to the server in the next request:
242 return agent.get('/user/me')
243 .then(function (res) {
244 expect(res).to.have.status(200);
245 });
246 });
247```
248
249Note: The server started by `chai.request.agent(app)` will not automatically close following the test(s). You should call `agent.close()` after your tests to ensure your program exits.
250
251## Assertions
252
253The Chai HTTP module provides a number of assertions
254for the `expect` and `should` interfaces.
255
256### .status (code)
257
258* **@param** _{Number}_ status number
259
260Assert that a response has a supplied status.
261
262```js
263expect(res).to.have.status(200);
264```
265
266### .header (key[, value])
267
268* **@param** _{String}_ header key (case insensitive)
269* **@param** _{String|RegExp}_ header value (optional)
270
271Assert that a `Response` or `Request` object has a header.
272If a value is provided, equality to value will be asserted.
273You may also pass a regular expression to check.
274
275__Note:__ When running in a web browser, the
276[same-origin policy](https://tools.ietf.org/html/rfc6454#section-3)
277only allows Chai HTTP to read
278[certain headers](https://www.w3.org/TR/cors/#simple-response-header),
279which can cause assertions to fail.
280
281```js
282expect(req).to.have.header('x-api-key');
283expect(req).to.have.header('content-type', 'text/plain');
284expect(req).to.have.header('content-type', /^text/);
285```
286
287### .headers
288
289
290Assert that a `Response` or `Request` object has headers.
291
292__Note:__ When running in a web browser, the
293[same-origin policy](https://tools.ietf.org/html/rfc6454#section-3)
294only allows Chai HTTP to read
295[certain headers](https://www.w3.org/TR/cors/#simple-response-header),
296which can cause assertions to fail.
297
298```js
299expect(req).to.have.headers;
300```
301
302### .ip
303
304
305Assert that a string represents valid ip address.
306
307```js
308expect('127.0.0.1').to.be.an.ip;
309expect('2001:0db8:85a3:0000:0000:8a2e:0370:7334').to.be.an.ip;
310```
311
312### .json / .text / .html
313
314
315Assert that a `Response` or `Request` object has a given content-type.
316
317```js
318expect(req).to.be.json;
319expect(req).to.be.html;
320expect(req).to.be.text;
321```
322
323### .redirect
324
325
326Assert that a `Response` object has a redirect status code.
327
328```js
329expect(res).to.redirect;
330expect(res).to.not.redirect;
331```
332
333### .redirectTo
334
335* **@param** _{String|RegExp}_ location url
336
337Assert that a `Response` object redirects to the supplied location.
338
339```js
340expect(res).to.redirectTo('http://example.com');
341expect(res).to.redirectTo(/^\/search\/results\?orderBy=desc$/);
342```
343
344### .param
345
346* **@param** _{String}_ parameter name
347* **@param** _{String}_ parameter value
348
349Assert that a `Request` object has a query string parameter with a given
350key, (optionally) equal to value
351
352```js
353expect(req).to.have.param('orderby');
354expect(req).to.have.param('orderby', 'date');
355expect(req).to.not.have.param('limit');
356```
357
358### .cookie
359
360* **@param** _{String}_ parameter name
361* **@param** _{String}_ parameter value
362
363Assert that a `Request` or `Response` object has a cookie header with a
364given key, (optionally) equal to value
365
366```js
367expect(req).to.have.cookie('session_id');
368expect(req).to.have.cookie('session_id', '1234');
369expect(req).to.not.have.cookie('PHPSESSID');
370expect(res).to.have.cookie('session_id');
371expect(res).to.have.cookie('session_id', '1234');
372expect(res).to.not.have.cookie('PHPSESSID');
373```
374
375## License
376
377(The MIT License)
378
379Copyright (c) Jake Luer <jake@alogicalparadox.com>
380
381Permission is hereby granted, free of charge, to any person obtaining a copy
382of this software and associated documentation files (the "Software"), to deal
383in the Software without restriction, including without limitation the rights
384to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
385copies of the Software, and to permit persons to whom the Software is
386furnished to do so, subject to the following conditions:
387
388The above copyright notice and this permission notice shall be included in
389all copies or substantial portions of the Software.
390
391THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
392IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
393FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
394AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
395LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
396OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
397THE SOFTWARE.
398