问题
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:
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?- 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?
- 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.
module
is the current /lib/app.js script.module.parent
is its parent /index.js script.module.parent.parent
isundefined
if the application was started directly vianode index.js
.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