Chai HTTP
HTTP integration testing with Chai assertions.
Features
- integration test request composition
- test http apps or external services
- assertions for common http tasks
- chai
expect
and should
interfaces
Installation
This is an addon plugin for the Chai Assertion Library. Install via npm.
npm install chai-http
Plugin
Use this plugin as you would all other Chai plugins.
import * as chai from "chai";
import chaiHttp from "chai-http";
chai.use(chaiHttp);
import {default as chaiHttp, request} from "chai-http";
chai.use(chaiHttp);
request.get(...).send(...);
request.execute(app);
To use Chai HTTP on a web page, please use the latest v4 version for now.
Integration Testing
Chai HTTP provides an interface for live integration
testing via superagent.
To do this, you must first
construct a request to an application or url.
Upon construction you are provided a chainable api that
allows you to specify the http VERB request (get, post, etc)
that you wish to invoke.
Application / Server
You may use a function (such as an express or connect app)
or a node.js http(s) server as the foundation for your request.
If the server is not running, chai-http will find a suitable
port to listen on for a given test.
Note: This feature is only supported on Node.js, not in web browsers.
import {request} from 'chai-http';
request.execute(app)
.get('/')
When passing an app
to request
; it will automatically open the server for
incoming requests (by calling listen()
) and, once a request has been made
the server will automatically shut down (by calling .close()
). If you want to
keep the server open, perhaps if you're making multiple requests, you must call
.keepOpen()
after .request()
, and manually close the server down:
import {request} from 'chai-http';
const requester = request.Request(app).keepOpen()
Promise.all([
requester.get('/a'),
requester.get('/b'),
])
.then(responses => { })
.then(() => requester.close())
URL
You may also use a base url as the foundation of your request.
import {request} from 'chai-http';
request.execute('http://localhost:8080')
.get('/')
Setting up requests
Once a request is created with a given VERB (get, post, etc), you chain on these additional methods to create your request:
Method | Purpose |
---|
.set(key, value) | Set request headers |
.send(data) | Set request data (default type is JSON) |
.type(dataType) | Change the type of the data sent from the .send() method (xml, form, etc) |
.attach(field, file, attachment) | Attach a file |
.auth(username, password) | Add auth headers for Basic Authentication |
.query(parmasObject) | Chain on some GET parameters |
Examples:
.set()
import {request} from 'chai-http';
request.execute(app)
.put('/user/me')
.set('Content-Type', 'application/json')
.send({ password: '123', confirmPassword: '123' })
.send()
import {request} from 'chai-http';
request.execute(app)
.put('/user/me')
.send({ password: '123', confirmPassword: '123' })
.type()
import {request} from 'chai-http';
request.execute(app)
.post('/user/me')
.type('form')
.send({
'_method': 'put',
'password': '123',
'confirmPassword': '123'
})
.attach()
import {request} from 'chai-http';
request.execute(app)
.post('/user/avatar')
.attach('imageField', fs.readFileSync('avatar.png'), 'avatar.png')
.auth()
import {request} from 'chai-http';
request.execute(app)
.get('/protected')
.auth('user', 'pass')
request.execute(app)
.get('/protected')
.auth(accessToken, { type: 'bearer' })
.query()
import {request} from 'chai-http';
request.execute(app)
.get('/search')
.query({name: 'foo', limit: 10})
Dealing with the response - traditional
In the following examples we use Chai's Expect assertion library:
const { expect } = chai;
To make the request and assert on its response, the end
method can be used:
import {request} from 'chai-http';
request.execute(app)
.put('/user/me')
.send({ password: '123', confirmPassword: '123' })
.end((err, res) => {
expect(err).to.be.null;
expect(res).to.have.status(200);
});
Caveat
Because the end
function is passed a callback, assertions are run
asynchronously. Therefore, a mechanism must be used to notify the testing
framework that the callback has completed. Otherwise, the test will pass before
the assertions are checked.
For example, in the Mocha test framework, this is
accomplished using the
done
callback, which signal that the
callback has completed, and the assertions can be verified:
import {request} from 'chai-http';
it('fails, as expected', function(done) {
request.execute('http://localhost:8080')
.get('/')
.end((err, res) => {
expect(res).to.have.status(123);
done();
});
});
it('succeeds silently!', () => {
request.execute('http://localhost:8080')
.get('/')
.end((err, res) => {
expect(res).to.have.status(123);
});
});
When done
is passed in, Mocha will wait until the call to done()
, or until
the timeout expires. done
also accepts an
error parameter when signaling completion.
Dealing with the response - Promises
If Promise
is available, request()
becomes a Promise capable library -
and chaining of then
s becomes possible:
import {request} from 'chai-http';
request.execute(app)
.put('/user/me')
.send({ password: '123', confirmPassword: '123' })
.then((res) => {
expect(res).to.have.status(200);
})
.catch((err) => {
throw err;
});
Retaining cookies with each request
Sometimes you need to keep cookies from one request, and send them with the
next (for example, when you want to login with the first request, then access an authenticated-only resource later). For this, .request.agent()
is available:
import {request} from 'chai-http';
const agent = request.agent(app)
agent
.post('/session')
.send({ username: 'me', password: '123' })
.then((res) => {
expect(res).to.have.cookie('sessionid');
return agent.get('/user/me')
.then((res) => {
expect(res).to.have.status(200);
});
});
Note: The server started by request.agent(app)
will not automatically close following the test(s). You should call agent.close()
after your tests to ensure your program exits.
Assertions
The Chai HTTP module provides a number of assertions
for the expect
and should
interfaces.
.status (code)
- @param {Number} status number
Assert that a response has a supplied status.
expect(res).to.have.status(200);
- @param {String} header key (case insensitive)
- @param {String|RegExp} header value (optional)
Assert that a Response
or Request
object has a header.
If a value is provided, equality to value will be asserted.
You may also pass a regular expression to check.
Note: When running in a web browser, the
same-origin policy
only allows Chai HTTP to read
certain headers,
which can cause assertions to fail.
expect(req).to.have.header('x-api-key');
expect(req).to.have.header('content-type', 'text/plain');
expect(req).to.have.header('content-type', /^text/);
Assert that a Response
or Request
object has headers.
Note: When running in a web browser, the
same-origin policy
only allows Chai HTTP to read
certain headers,
which can cause assertions to fail.
expect(req).to.have.headers;
.ip
Assert that a string represents valid ip address.
expect('127.0.0.1').to.be.an.ip;
expect('2001:0db8:85a3:0000:0000:8a2e:0370:7334').to.be.an.ip;
.json / .text / .html
Assert that a Response
or Request
object has a given content-type.
expect(req).to.be.json;
expect(req).to.be.html;
expect(req).to.be.text;
.charset
Assert that a Response
or Request
object has a given charset.
expect(req).to.have.charset('utf-8');
.redirect
Assert that a Response
object has a redirect status code.
expect(res).to.redirect;
expect(res).to.not.redirect;
.redirectTo
- @param {String|RegExp} location url
Assert that a Response
object redirects to the supplied location.
expect(res).to.redirectTo('http://example.com');
expect(res).to.redirectTo(/^\/search\/results\?orderBy=desc$/);
.param
- @param {String} parameter name
- @param {String} parameter value
Assert that a Request
object has a query string parameter with a given
key, (optionally) equal to value
expect(req).to.have.param('orderby');
expect(req).to.have.param('orderby', 'date');
expect(req).to.not.have.param('limit');
.cookie
- @param {String} parameter name
- @param {String} parameter value
Assert that a Request
or Response
object has a cookie header with a
given key, (optionally) equal to value
expect(req).to.have.cookie('session_id');
expect(req).to.have.cookie('session_id', '1234');
expect(req).to.not.have.cookie('PHPSESSID');
expect(res).to.have.cookie('session_id');
expect(res).to.have.cookie('session_id', '1234');
expect(res).to.not.have.cookie('PHPSESSID');
Releasing
chai-http
is released with semantic-release
using the plugins:
License
(The MIT License)
Copyright (c) Jake Luer jake@alogicalparadox.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.