Ensuring Express App is running before each Mocha Test
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');// databasemongoose.connect(config.db);// middlewaremiddleware(app);// Application routesroutes(app);app.listen(app.get('port'));console.log('Express server listening on port ' + app.get('port'));// export app so we can test itexports = 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
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 testingsetTimeout(() => { 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