Setup/teardown of express.js application with mocha
Asked Answered
V

2

18

I'm trying to create a standalone test suite using mocha, that in a perfect world would start up my express.js application, use zombie to render a page, check a bunch of stuff and then teardown/kill the express.js application.

Is there an easy/best way to do this?

NB. I could just have the express application server running prior to running the tests, but what good are Yaks if you're not going to shave them.

Vaduz answered 10/6, 2012 at 2:29 Comment(1)
Nice question. I myself looked into that for a while but after a week I just gave up. I found zombie really annoying to work with. I read a lot about PhantomJS but I didn't try it myself.Yoon
C
29

First, you need to move your actual app setting up into a module, and import that into the file that actually starts your app. Now that this is seperate, you can have the app in its complete state before actually listening.

You should move the actual setting up of your app into a separate file, let's call it app.js, can call listen from the file you run node off of, let's call it index.js.

So, app.js would look like:

    var express = require('express')
  , routes = require('./routes');

var app = module.exports = express.createServer();

// Configuration

app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  app.use(express.errorHandler());
});

// Routes

app.get('/', routes.index);

and index.js would look like:

var app = require('./app');

app.listen(3000, function(){
  console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
});

This seperates loading of your app from actually having it listen, allowing you to load that app into your unit tests.

In your unit tests, you would do something in a setup method and teardown method to bring up and bring down the server.

In the file test/app_tests.js:

describe('app', function(){
  var app = require('../app');
  beforeEach(function(){
    app.listen(3000);
  });
  // tests here
  afterEach(function(){
    app.close();
  })
});
Clementius answered 17/7, 2012 at 23:7 Comment(4)
Awesome ! But, in my case, I have multiple test files and it seems that mocha executes all files concurrently, which causes some bad conflicts between test files! what can I do ?Ridotto
Make each test listen on a different port - just pick a random number (high enough)Trounce
EXCELLENT SOLUTION! This is the right way to do it. One modification: you should actually be closing the result of app.listen, not app itself, ie: (in beforeEach) "instance = app.listen(3000);" ...and then (in afterEach) "instance.close()"Hardison
You could also do sth like if (!module.parent) { server.listen(8080); } to prevent the server from starting in tests (and doing this manually before and after each test)Geisel
H
4

In addition to Oved D answer.

Describe your app in express-app.js or some other file:

module.exports = function (o) {
  o = o || {};
  var app = express();

  // app.configure
  // configure routes
  if (o.someOption) {
    // some additional test code
  }

  return app;
}

describe tests in test/01-some.js:

var expressApp = require('../express-app');
describe('some', function () {

  // just describe needed vars
  var app, server, port;

  // describe setup
  before(function (next) {
    // create app when 
    app = expressApp({routes: /api\/some\/.*/ /* put here some test options ...*/});
    // creating listener with random port
    server = app.listen(function () {
      // store port when it ready
      port = server.address().port;
      // and go to tests
      next();
    });
  });

  // tests
  it('should return valid result', function (done) {
    // do a simple request to /api/some
    http.request({
      host: 'localhost',
      port: port,
      path: '/api/some'
    }, function (res) {
      if (res.err) throw new Error(res.err);
      done();
    });
  });

  // teardown
  after(function () {
    // stop listening that port
    server.close();
  });

});

Done. ;-)

Now you can create any count of tests like that. Recommend you to enable only needed urls and services in tests with defining it by passing params to express-app.js module.


Update:

Not sure how it works in mocha but better to move before and after calls to init.js and load it with mocha --require init.js.

File should looks like that:

// use it in your mocha tests
global.setupEnv = function setupEnv (o, before, after) {

    // just describe needed vars
    var env = Object.create(null);

    // setup
    before(function (next) {
        // create app
        env.app = expressApp(o);
        // creating listener with random port
        env.server = env.app.listen(function () {
            // store port when it ready
            port = env.server.address().port;
            env.app.log('Listening on ', env.port);
            // and go to tests
            next();
        });
    });

    // teardown
    after(function () {
        // stop listening that port
        env.server.close();
    });

    return env;
}

And in your tests:

// requiring dependencies
var request = require('request');

describe('api', function () {

    // describe setup
    var env = global.setupEnv({
            routes: 'api/some'
        }, before, after);

    // tests
    it('should pass', function (done) {
        request('http://localhost:' + env.port, function (error, response, body) {
            done();
        });
    });

});
Hybridism answered 1/7, 2014 at 15:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.