How do i test my express app with mocha?
node.jsExpressmocha.jsnode.js Problem Overview
I've just added shouldjs and mocha to my express app for testing, but I'm wondering how to test my application. I would like to do it like this:
app = require '../app'
routes = require '../src/routes'
describe 'routes', ->
describe '#show_create_user_screen', ->
it 'should be a function', ->
routes.show_create_user_screen.should.be.a.function
it 'should return something cool', ->
routes.show_create_user_screen().should.be.an.object
Of course, the last test in that test-suite just tells med that the res.render function (called within show_create_user_screen) is undefined, probably becouse the server is not running and the config has not been done. So I wonder how other people set up their tests?
node.js Solutions
Solution 1 - node.js
found an alternative in connect.js tests suites
They are using supertest to test a connect app without binding the server to any port and without using mock-ups.
Here is an excerpt from connect's static middleware test suite (using mocha as the test runner and supertest for assertions)
var connect = require('connect');
var app = connect();
app.use(connect.static(staticDirPath));
describe('connect.static()', function(){
it('should serve static files', function(done){
app.request()
.get('/todo.txt')
.expect('contents', done);
})
});
This works for express apps as well
Solution 2 - node.js
OK, first although testing your routing code is something you may or may not want to do, in general, try to separate your interesting business logic in pure javascript code (classes or functions) that are decoupled from express or whatever framework you are using and use vanilla mocha tests to test that. Once you've achieved that if you want to really test the routes you configure in mocha, you need to pass mock req, res
parameters into your middleware functions to mimic the interface between express/connect and your middleware.
For a simple case, you could create a mock res
object with a render
function that looks something like this.
describe 'routes', ->
describe '#show_create_user_screen', ->
it 'should be a function', ->
routes.show_create_user_screen.should.be.a.function
it 'should return something cool', ->
mockReq = null
mockRes =
render: (viewName) ->
viewName.should.exist
viewName.should.match /createuser/
routes.show_create_user_screen(mockReq, mockRes).should.be.an.object
Also just FYI middleware functions don't need to return any particular value, it's what they do with the req, res, next
parameters that you should focus on in testing.
Here is some JavaScript as you requested in the comments.
describe('routes', function() {
describe('#show_create_user_screen', function() {
it('should be a function', function() {
routes.show_create_user_screen.should.be.a["function"];
});
it('should return something cool', function() {
var mockReq = null;
var mockRes = {
render: function(viewName) {
viewName.should.exist;
viewName.should.match(/createuser/);
}
};
routes.show_create_user_screen(mockReq, mockRes);
});
});
});
Solution 3 - node.js
You could try SuperTest, and then server start-up and shutdown are taken care of:
var request = require('supertest')
, app = require('./anExpressServer').app
, assert = require("assert");
describe('POST /', function(){
it('should fail bad img_uri', function(done){
request(app)
.post('/')
.send({
'img_uri' : 'foobar'
})
.expect(500)
.end(function(err, res){
done();
})
})
});
Solution 4 - node.js
mocha comes with before, beforeEach, after, and afterEach for bdd testing. In this case you should use before in your describe call.
describe 'routes' ->
before (done) ->
app.listen(3000)
app.on('connection', done)
Solution 5 - node.js
I've found it's easiest to set up a TestServer class to be used as a helper, as well as a helper http client, and just make real requests to a real http server. There may be cases where you want to mock and stub this stuff instead though.
// Test file
var http = require('the/below/code');
describe('my_controller', function() {
var server;
before(function() {
var router = require('path/to/some/router');
server = http.server.create(router);
server.start();
});
after(function() {
server.stop();
});
describe("GET /foo", function() {
it('returns something', function(done) {
http.client.get('/foo', function(err, res) {
// assertions
done();
});
});
});
});
// Test helper file
var express = require('express');
var http = require('http');
// These could be args passed into TestServer, or settings from somewhere.
var TEST_HOST = 'localhost';
var TEST_PORT = 9876;
function TestServer(args) {
var self = this;
var express = require('express');
self.router = args.router;
self.server = express.createServer();
self.server.use(express.bodyParser());
self.server.use(self.router);
}
TestServer.prototype.start = function() {
var self = this;
if (self.server) {
self.server.listen(TEST_PORT, TEST_HOST);
} else {
throw new Error('Server not found');
}
};
TestServer.prototype.stop = function() {
var self = this;
self.server.close();
};
// you would likely want this in another file, and include similar
// functions for post, put, delete, etc.
function http_get(host, port, url, cb) {
var options = {
host: host,
port: port,
path: url,
method: 'GET'
};
var ret = false;
var req = http.request(options, function(res) {
var buffer = '';
res.on('data', function(data) {
buffer += data;
});
res.on('end',function(){
cb(null,buffer);
});
});
req.end();
req.on('error', function(e) {
if (!ret) {
cb(e, null);
}
});
}
var client = {
get: function(url, cb) {
http_get(TEST_HOST, TEST_PORT, url, cb);
}
};
var http = {
server: {
create: function(router) {
return new TestServer({router: router});
}
},
client: client
};
module.exports = http;