How to test a clustered Express app with Mocha?

你说的曾经没有我的故事 提交于 2020-01-31 15:30:15

问题


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?


回答1:


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.




回答2:


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




回答3:


I kind of liked your solution because of it's simplicity, however, in an environment like an MVC framework for node, you may end up chaining module.parent up to 11 times (seriously).

I think a better approach would be to simply check which script node started processing with. The node's command-line arguments are available at process.argv. The first item in this array would be 'node', the executable and the second argument would be the path to the file that node start executing. This would be index.js in your case.

So instead of checking

                   module.parent.parent
                            ^       ^        
                         (app.js)   |
                                (index.js)

You could do something like this

var starter = process.argv[1].split(path.sep).pop();

Where starter would be index or index.js depending on what you started your server with. node index.js vs node index

The check would then look like:

if (cluster.isMaster && starter === 'index.js') {
    cluster.fork();
}

Worked in my environments—I hope this helps!



来源:https://stackoverflow.com/questions/17737448/how-to-test-a-clustered-express-app-with-mocha

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!