1 | # SuperTest
|
2 |
|
3 | [![code coverage][coverage-badge]][coverage]
|
4 | [![Build Status][travis-badge]][travis]
|
5 | [![Dependencies][dependencies-badge]][dependencies]
|
6 | [![PRs Welcome][prs-badge]][prs]
|
7 | [![MIT License][license-badge]][license]
|
8 |
|
9 | > HTTP assertions made easy via [superagent](http://github.com/ladjs/superagent). Maintained for [Forward Email](https://github.com/forwardemail) and [Lad](https://github.com/ladjs).
|
10 |
|
11 | ## About
|
12 |
|
13 | The motivation with this module is to provide a high-level abstraction for testing
|
14 | HTTP, while still allowing you to drop down to the [lower-level API](https://ladjs.github.io/superagent/) provided by superagent.
|
15 |
|
16 | ## Getting Started
|
17 |
|
18 | Install SuperTest as an npm module and save it to your package.json file as a development dependency:
|
19 |
|
20 | ```bash
|
21 | npm install supertest --save-dev
|
22 | ```
|
23 |
|
24 | Once installed it can now be referenced by simply calling ```require('supertest');```
|
25 |
|
26 | ## Example
|
27 |
|
28 | You may pass an `http.Server`, or a `Function` to `request()` - if the server is not
|
29 | already listening for connections then it is bound to an ephemeral port for you so
|
30 | there is no need to keep track of ports.
|
31 |
|
32 | SuperTest works with any test framework, here is an example without using any
|
33 | test framework at all:
|
34 |
|
35 | ```js
|
36 | const request = require('supertest');
|
37 | const assert = require('assert');
|
38 | const express = require('express');
|
39 |
|
40 | const app = express();
|
41 |
|
42 | app.get('/user', function(req, res) {
|
43 | res.status(200).json({ name: 'john' });
|
44 | });
|
45 |
|
46 | request(app)
|
47 | .get('/user')
|
48 | .expect('Content-Type', /json/)
|
49 | .expect('Content-Length', '15')
|
50 | .expect(200)
|
51 | .end(function(err, res) {
|
52 | if (err) throw err;
|
53 | });
|
54 | ```
|
55 |
|
56 | To enable http2 protocol, simply append an options to `request` or `request.agent`:
|
57 |
|
58 | ```js
|
59 | const request = require('supertest');
|
60 | const express = require('express');
|
61 |
|
62 | const app = express();
|
63 |
|
64 | app.get('/user', function(req, res) {
|
65 | res.status(200).json({ name: 'john' });
|
66 | });
|
67 |
|
68 | request(app, { http2: true })
|
69 | .get('/user')
|
70 | .expect('Content-Type', /json/)
|
71 | .expect('Content-Length', '15')
|
72 | .expect(200)
|
73 | .end(function(err, res) {
|
74 | if (err) throw err;
|
75 | });
|
76 |
|
77 | request.agent(app, { http2: true })
|
78 | .get('/user')
|
79 | .expect('Content-Type', /json/)
|
80 | .expect('Content-Length', '15')
|
81 | .expect(200)
|
82 | .end(function(err, res) {
|
83 | if (err) throw err;
|
84 | });
|
85 | ```
|
86 |
|
87 | Here's an example with mocha, note how you can pass `done` straight to any of the `.expect()` calls:
|
88 |
|
89 | ```js
|
90 | describe('GET /user', function() {
|
91 | it('responds with json', function(done) {
|
92 | request(app)
|
93 | .get('/user')
|
94 | .set('Accept', 'application/json')
|
95 | .expect('Content-Type', /json/)
|
96 | .expect(200, done);
|
97 | });
|
98 | });
|
99 | ```
|
100 |
|
101 | You can use `auth` method to pass HTTP username and password in the same way as in the [superagent](http://ladjs.github.io/superagent/#authentication):
|
102 |
|
103 | ```js
|
104 | describe('GET /user', function() {
|
105 | it('responds with json', function(done) {
|
106 | request(app)
|
107 | .get('/user')
|
108 | .auth('username', 'password')
|
109 | .set('Accept', 'application/json')
|
110 | .expect('Content-Type', /json/)
|
111 | .expect(200, done);
|
112 | });
|
113 | });
|
114 | ```
|
115 |
|
116 | One thing to note with the above statement is that superagent now sends any HTTP
|
117 | error (anything other than a 2XX response code) to the callback as the first argument if
|
118 | you do not add a status code expect (i.e. `.expect(302)`).
|
119 |
|
120 | If you are using the `.end()` method `.expect()` assertions that fail will
|
121 | not throw - they will return the assertion as an error to the `.end()` callback. In
|
122 | order to fail the test case, you will need to rethrow or pass `err` to `done()`, as follows:
|
123 |
|
124 | ```js
|
125 | describe('POST /users', function() {
|
126 | it('responds with json', function(done) {
|
127 | request(app)
|
128 | .post('/users')
|
129 | .send({name: 'john'})
|
130 | .set('Accept', 'application/json')
|
131 | .expect('Content-Type', /json/)
|
132 | .expect(200)
|
133 | .end(function(err, res) {
|
134 | if (err) return done(err);
|
135 | return done();
|
136 | });
|
137 | });
|
138 | });
|
139 | ```
|
140 |
|
141 | You can also use promises:
|
142 |
|
143 | ```js
|
144 | describe('GET /users', function() {
|
145 | it('responds with json', function() {
|
146 | return request(app)
|
147 | .get('/users')
|
148 | .set('Accept', 'application/json')
|
149 | .expect('Content-Type', /json/)
|
150 | .expect(200)
|
151 | .then(response => {
|
152 | assert(response.body.email, 'foo@bar.com')
|
153 | })
|
154 | });
|
155 | });
|
156 | ```
|
157 |
|
158 | Or async/await syntax:
|
159 |
|
160 | ```js
|
161 | describe('GET /users', function() {
|
162 | it('responds with json', async function() {
|
163 | const response = await request(app)
|
164 | .get('/users')
|
165 | .set('Accept', 'application/json')
|
166 | expect(response.headers["Content-Type"]).toMatch(/json/);
|
167 | expect(response.status).toEqual(200);
|
168 | expect(response.body.email).toEqual('foo@bar.com');
|
169 | });
|
170 | });
|
171 | ```
|
172 |
|
173 | Expectations are run in the order of definition. This characteristic can be used
|
174 | to modify the response body or headers before executing an assertion.
|
175 |
|
176 | ```js
|
177 | describe('POST /user', function() {
|
178 | it('user.name should be an case-insensitive match for "john"', function(done) {
|
179 | request(app)
|
180 | .post('/user')
|
181 | .send('name=john') // x-www-form-urlencoded upload
|
182 | .set('Accept', 'application/json')
|
183 | .expect(function(res) {
|
184 | res.body.id = 'some fixed id';
|
185 | res.body.name = res.body.name.toLowerCase();
|
186 | })
|
187 | .expect(200, {
|
188 | id: 'some fixed id',
|
189 | name: 'john'
|
190 | }, done);
|
191 | });
|
192 | });
|
193 | ```
|
194 |
|
195 | Anything you can do with superagent, you can do with supertest - for example multipart file uploads!
|
196 |
|
197 | ```js
|
198 | request(app)
|
199 | .post('/')
|
200 | .field('name', 'my awesome avatar')
|
201 | .field('complex_object', '{"attribute": "value"}', {contentType: 'application/json'})
|
202 | .attach('avatar', 'test/fixtures/avatar.jpg')
|
203 | ...
|
204 | ```
|
205 |
|
206 | Passing the app or url each time is not necessary, if you're testing
|
207 | the same host you may simply re-assign the request variable with the
|
208 | initialization app or url, a new `Test` is created per `request.VERB()` call.
|
209 |
|
210 | ```js
|
211 | request = request('http://localhost:5555');
|
212 |
|
213 | request.get('/').expect(200, function(err){
|
214 | console.log(err);
|
215 | });
|
216 |
|
217 | request.get('/').expect('heya', function(err){
|
218 | console.log(err);
|
219 | });
|
220 | ```
|
221 |
|
222 | Here's an example with mocha that shows how to persist a request and its cookies:
|
223 |
|
224 | ```js
|
225 | const request = require('supertest');
|
226 | const should = require('should');
|
227 | const express = require('express');
|
228 | const cookieParser = require('cookie-parser');
|
229 |
|
230 | describe('request.agent(app)', function() {
|
231 | const app = express();
|
232 | app.use(cookieParser());
|
233 |
|
234 | app.get('/', function(req, res) {
|
235 | res.cookie('cookie', 'hey');
|
236 | res.send();
|
237 | });
|
238 |
|
239 | app.get('/return', function(req, res) {
|
240 | if (req.cookies.cookie) res.send(req.cookies.cookie);
|
241 | else res.send(':(')
|
242 | });
|
243 |
|
244 | const agent = request.agent(app);
|
245 |
|
246 | it('should save cookies', function(done) {
|
247 | agent
|
248 | .get('/')
|
249 | .expect('set-cookie', 'cookie=hey; Path=/', done);
|
250 | });
|
251 |
|
252 | it('should send cookies', function(done) {
|
253 | agent
|
254 | .get('/return')
|
255 | .expect('hey', done);
|
256 | });
|
257 | });
|
258 | ```
|
259 |
|
260 | There is another example that is introduced by the file [agency.js](https://github.com/ladjs/superagent/blob/master/test/node/agency.js)
|
261 |
|
262 | Here is an example where 2 cookies are set on the request.
|
263 |
|
264 | ```js
|
265 | agent(app)
|
266 | .get('/api/content')
|
267 | .set('Cookie', ['nameOne=valueOne;nameTwo=valueTwo'])
|
268 | .send()
|
269 | .expect(200)
|
270 | .end((err, res) => {
|
271 | if (err) {
|
272 | return done(err);
|
273 | }
|
274 | expect(res.text).to.be.equal('hey');
|
275 | return done();
|
276 | });
|
277 | ```
|
278 |
|
279 | ## API
|
280 |
|
281 | You may use any [superagent](http://github.com/ladjs/superagent) methods,
|
282 | including `.write()`, `.pipe()` etc and perform assertions in the `.end()` callback
|
283 | for lower-level needs.
|
284 |
|
285 | ### .expect(status[, fn])
|
286 |
|
287 | Assert response `status` code.
|
288 |
|
289 | ### .expect(status, body[, fn])
|
290 |
|
291 | Assert response `status` code and `body`.
|
292 |
|
293 | ### .expect(body[, fn])
|
294 |
|
295 | Assert response `body` text with a string, regular expression, or
|
296 | parsed body object.
|
297 |
|
298 | ### .expect(field, value[, fn])
|
299 |
|
300 | Assert header `field` `value` with a string or regular expression.
|
301 |
|
302 | ### .expect(function(res) {})
|
303 |
|
304 | Pass a custom assertion function. It'll be given the response object to check. If the check fails, throw an error.
|
305 |
|
306 | ```js
|
307 | request(app)
|
308 | .get('/')
|
309 | .expect(hasPreviousAndNextKeys)
|
310 | .end(done);
|
311 |
|
312 | function hasPreviousAndNextKeys(res) {
|
313 | if (!('next' in res.body)) throw new Error("missing next key");
|
314 | if (!('prev' in res.body)) throw new Error("missing prev key");
|
315 | }
|
316 | ```
|
317 |
|
318 | ### .end(fn)
|
319 |
|
320 | Perform the request and invoke `fn(err, res)`.
|
321 |
|
322 | ## Notes
|
323 |
|
324 | Inspired by [api-easy](https://github.com/flatiron/api-easy) minus vows coupling.
|
325 |
|
326 | ## License
|
327 |
|
328 | MIT
|
329 |
|
330 | [coverage-badge]: https://img.shields.io/codecov/c/github/ladjs/supertest.svg
|
331 | [coverage]: https://codecov.io/gh/ladjs/supertest
|
332 | [travis-badge]: https://travis-ci.org/ladjs/supertest.svg?branch=master
|
333 | [travis]: https://travis-ci.org/ladjs/supertest
|
334 | [dependencies-badge]: https://david-dm.org/ladjs/supertest/status.svg
|
335 | [dependencies]: https://david-dm.org/ladjs/supertest
|
336 | [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
|
337 | [prs]: http://makeapullrequest.com
|
338 | [license-badge]: https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square
|
339 | [license]: https://github.com/ladjs/supertest/blob/master/LICENSE
|