I am working on developing a REST API using ExpressJS, NodeJS, Mongoose and Mocha.
The thing is that I have an app.coffee file, thats responsible for setting up Expr
I am late to the party, but I found the best way to set up my mocha test suite for an express app is to make my app.js or server.js file export the app object, like this:
var mongoose = require('mongoose');
var express = require('express');
require('express-mongoose');
var env = process.env.NODE_ENV || 'development';
var config = require('./config/config')[env];
var models = require('./app/models');
var middleware = require('./app/middleware');
var routes = require('./app/routes');
var app = express();
app.set('port', process.env.PORT || config.port || 3000);
app.set('views', __dirname + '/app/views');
app.set('view engine', 'jade');
// database
mongoose.connect(config.db);
// middleware
middleware(app);
// Application routes
routes(app);
app.listen(app.get('port'));
console.log('Express server listening on port ' + app.get('port'));
// export app so we can test it
exports = module.exports = app;
make sure your config file has different environments like development, test, production set up:
var path = require('path');
var rootPath = path.normalize(__dirname + '/..');
module.exports = {
development: {
db: 'mongodb://localhost/my_dev_db',
port: 3000
},
test: {
db: 'mongodb://localhost/my_test_db',
port: 8888
},
production: {
// ...
}
}
then in your test files you can go ahead and require your app, which will connect to the right db and on the right port:
var should = require('chai').should();
var request = require('supertest');
var mongoose = require('mongoose');
var app = require('../app');
var agent = request.agent(app);
var User = mongoose.model('User');
// get users
describe('GET /api/users', function() {
it('returns users as JSON', function(done) {
agent
.get('/api/users')
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) return done(err);
res.body.should.have.property('users').and.be.instanceof(Array);
done();
});
});
});
And finally, to start up the whole monster you include this in your package.json
(make sure to have nodemon and mocha in your devDependencies):
"scripts": {
"start": "NODE_ENV=development ./node_modules/.bin/nodemon app.js",
"test": "NODE_ENV=test ./node_modules/.bin/mocha --reporter spec test/**.js"
}
Now you can start your test suite with npm test
and your app with npm start
.
Hope it helps! ps: most of the stuff I learned from looking at this amazing example: https://github.com/madhums/node-express-mongoose-demo
You don't need to listen a port for testing your app. You can use supertest testing library with your app and it should be ok.
You would probably need to connect to a database or redis client though. You can do this in configure
method of your app. Since node caches modules, you can require app module in different test modules without reconnecting.
I ran into the same issue while using jasmine/supertest to test my express app. I solved the issue by emitting when the app was ready and only running my tests afterwards. Here is my directory structure
- server.js
- spec
- setup.spec.js
- test.spec.js
In server.js
when ever your server is set up and you are ready to run your tests add app.emit('started');
. And make sure to export your app! In setup.spec.js
you can watch for the event to be emitted.
server.js
const express = require('express');
const app = express();
module.exports = app; // for testing
setTimeout(() => {
app.listen(process.env.PORT || 3000);
});
setup.spec.js
const server = require('../index');
beforeAll(done => server.on('started', done));
test.spec.js
const request = require('supertest');
const server = require('../index');
describe('test', () => {
it('test server works', done => {
request(server).get('/test').expect(200);
});
});
The same idea should work for mocha as well. Here is an article explaining this for mocha. You should just change beforeAll
to before
.
There might be a more straightforward way, but I went to Grunt for automating my functional tests. There's an express and mocha plugin to reach your goal. My gruntfile:
'use strict';
module.exports = function (grunt) {
grunt.initConfig({
express: {
options: {}
, test: {
options: {
script: './app.js'
}
}
}
, simplemocha: {
options: {
globals: ['should']
, timeout: 8000
, ignoreLeaks: false
, ui: 'bdd'
, reporter: 'tap'
}
, all: { src: ['tests/*.test.js'] }
}
})
grunt.loadNpmTasks('grunt-express-server')
grunt.loadNpmTasks('grunt-simple-mocha')
grunt.registerTask('default', ['express:test', 'simplemocha', 'express:test:stop'])
}
bonus: add 'grunt' as a git pre-commit hook. This way you cannot commit without passing all the tests
The app.listen method takes a callback that runs when everything is ready. So, you need to be able to pass the done callback there. Something like
before (done) ->
var app = require(__dirname + '/../src/app')
app.listen(3000, done)
Basically you need there are two things you need to do.
The framework already has an a way to handle this by using events.
Add this to your database connection, which instructs mongoose to emit 'ready' when it's finished connecting.
mongoose.connection.once('open', function() {
// All OK - fire (emit) a ready event.
console.log('Connected to MongoDB');
app.emit('ready');
});
Then instruct express to wait for 'ready' before listening. When it is done it emits: 'appStarted'.
app.on('ready', function() {
app.listen(PORT, function(){
console.log("Server listing on port:",PORT);
app.emit("appStarted");
});
});
This is all good practice Mocha aside, to make sure your server starts smoothly. To finish the Mocha integration in your before (I like to use Promises):
before(function() {
return new Promise((resolve,reject) => {
app.on("appStarted", function(){
return resolve();
});
});
});