supertest提供了简单易用的客户代理,可以方便地用来测试Express.js应用。 本文介绍如何在Mocha中使用supertest, 以及测试Express应用涉及到的一些问题: 包括如何在每个测试项之前正确地关闭上一个Express Server, 以及如何避免require缓存造成的环境差异。
关于如何使用Mocha测试Node.js代码,请参考利用 Mocha 进行 BDD 风格测试一文。
一个简易的Express应用
先来写一个简单的Express应用,绑定/目录并返回200 harttle。 下文中便来讨论如何正确地测试该应用。
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.status(200).send('harttle');
});
var server = app.listen(3000, function () {
var port = server.address().port;
console.log('Example app listening at port %s', port);
});
module.exports = server;
Supertest
supertest提供了.get(), .expect()等方法来测试 Express server。 下面给出一个简单的supertest使用示例。
var request = require('supertest');
describe('loading express', function () {
var server;
beforeEach(function () {
server = require('./server');
});
afterEach(function () {
server.close();
});
it('responds to /', function testSlash(done) {
request(server)
.get('/')
.expect(200, done);
});
it('404 everything else', function testPath(done) {
request(server)
.get('/foo/bar')
.expect(404, done);
});
});
正确关闭 Express
通常我们希望每项测试都在初始的环境中进行, 于是在每项测试后使用server.close()关闭服务器, 在每项测试前使用server = require('./server')重新开启。 然而server.close()不会立即关闭服务器,这使得测试中常发生EADDRINUSE错误。
但server.close()可以接受一个回调来通知服务器正确关闭事件。 可以利用该回调来使Mocha顺序执行异步过程。只需要将afterEach改为:
afterEach(function (done) {
server.close(done);
});
清除 require 缓存
尽管我们重启了Expressserver,当require('./server')时Node.js 仍然会返回上次require的缓存。通常的实践中应当用工厂方法来解决, 比如给出makeServer()方法:
function makeServer() {
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.status(200).send('harttle');
});
var server = app.listen(3000, function () {
var port = server.address().port;
console.log('Example app listening at port %s', port);
});
return server;
}
module.exports = makeServer;
这样每次调用makeServer()都会重新执行上述代码。 然而为了写测试去更改代码结构有时并不可行,正确的方式是通过Node.js API来清除缓存:
beforeEach(function () {
delete require.cache[require.resolve('./server')];
server = require('./server');
});
其实有一个NPM库really-need已经封装了上述代码,导入该工具即可覆盖require的默认行为。
require = require('really-need');
beforeEach(function () {
server = require('./server', { bustCache: true });
});
最终代码
最终的测试代码如下:
var request = require('supertest');
require = require('really-need');
describe('loading express', function () {
var server;
beforeEach(function () {
server = require('./server', { bustCache: true });
});
afterEach(function (done) {
server.close(done);
});
it('responds to /', function testSlash(done) {
request(server)
.get('/')
.expect(200, done);
});
it('404 everything else', function testPath(done) {
console.log('test 404')
request(server)
.get('/foo/bar')
.expect(404, done);
});
});