Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test a clustered Express app with Mocha?

Here is a simplified version of my cluster Express app:

/index.js

module.exports = process.env.CODE_COV
    ? require('./lib-cov/app')
    : require('./lib/app');

/lib/app.js

var cluster = require('cluster'),
    express = require('express'),
    app = module.exports = express.createServer();

if (cluster.isMaster) {
    // Considering I have 4 cores.
    for (var i = 0; i < 4; ++i) {
        cluster.fork();
    }
} else {
    // do app configurations, then...

    // Don't listen to this port if the app is required from a test script.
    if (!module.parent.parent) {
        app.listen(8080);
    }
}

/test/test1.js

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

app.listen(7777);

// send requests to app, then assert the response.

Questions:

  1. var app = require('../'); will not work in this cluster environment. Which of the worker apps should it return? Should it return the cluster object instead of an Express app?
  2. Now, obviously setting the port in the test script will not work. How would you set a port within a test script to a cluster of apps?
  3. How would you send requests to this cluster of apps?

The only solution I can think of is to conditionally turn off the clustering feature and run only one app if the app is requested from a test script (if (module.parent.parent) ...).

Any other way to test a clustered Express app with Mocha?

like image 353
Eye Avatar asked Jul 19 '13 03:07

Eye


2 Answers

It's been quite a long time since I have posted this question. Since no one has answered, I will answer to this question myself.

I kept the /index.js as it is:

module.exports = process.env.CODE_COV
    ? require('./lib-cov/app')
    : require('./lib/app');

In /lib/app.js which starts the cluster, I have the following code. In brief, I start the cluster only in non-test environment. In test environment the cluster is not started but only one app/worker itself is started as defined in the cluster.isMaster && !module.parent.parent condition.

var cluster = require('cluster'),
    express = require('express'),
    app = module.exports = express.createServer();

if (cluster.isMaster && !module.parent.parent) {
    // Considering I have 4 cores.
    for (var i = 0; i < 4; ++i) {
        cluster.fork();
    }
} else {
    // do app configurations, then...

    // Don't listen to this port if the app is required from a test script.
    if (!module.parent.parent) {
        app.listen(8080);
    }
}

In the above case !module.parent.parent will be evaluated as a truthful object only if the application was not started by a test script.

  1. module is the current /lib/app.js script.
  2. module.parent is its parent /index.js script.
  3. module.parent.parent is undefined if the application was started directly via node index.js.
  4. module.parent.parent is the test script if the application was started via one of the scripts.

Thus, I can safely start the script where I can set a custom port.

/test/test1.js

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

app.listen(7777);

// send requests to app, then assert the response.

At the same time if I need to run the application in real, i.e. not for testing, then I run node index.js and it will start up the cluster of applications.

like image 191
Eye Avatar answered Nov 05 '22 02:11

Eye


I have a much simpler way of doing this

    if (process.env.NODE_ENV !== 'test') {
    if (cluster.isMaster) {
        var numCPUs = require('os').cpus().length;
        console.log('total cpu cores on this host: ', numCPUs);
        for (var i = 0; i < numCPUs; i++) {
            console.log('forking worker...');
            cluster.fork();
        }

        cluster.on('online', function(worker) {
            console.log('Worker ' + worker.process.pid + ' is online.');
        });
        cluster.on('exit', function(worker, code, signal) {
            console.log('worker ' + worker.process.pid + ' died.');
        });
    } else {
        console.log('Im a worker');
        // application code 
        setupServer()
    }
} else {
    // when running tests
    setupServer();
    }

Just make sure to set the env to test when running the tests ex: NODE_ENV=test grunt test

like image 41
Sudhakar Avatar answered Nov 05 '22 00:11

Sudhakar