This is similar to Ensuring Express App is running before each Mocha Test , but the specified solution still isnt working + i'm using a websocket server
in short , i'm using a websocket framework called socketcluster and this is my server file
import {SocketCluster} from 'socketcluster';
const socketCluster = new SocketCluster({
workers:1,
brokers:1,
port: 3000,
appName:null,
initController: __dirname + '/init.js',
workerController: __dirname + '/worker.js',
brokerController: __dirname + '/broker.js',
socketChannelLimit: 1000,
crashWorkerOnError: true
})
export default socketCluster
running node server.js
starts the worker process specified in worker.js
export const run = (worker) => {
console.log(' >> worker PID: ',process.pid);
const app = express();
const httpServer = worker.httpServer;
const scServer = worker.scServer;
app.use(cookieParser())
httpServer.on('request', app);
app.get('/',(req,res) => {
console.log('recieved')
res.send('Hello world')
})
}
I want to test the server , but the tests are finishing (and failing) way before the server actually starts. is there a way i can force the server to fully load before going ahead with tests? this is what i have so far
describe('Express server',() =>{
beforeEach((done) => {
require('../../server/server')
done()
})
it('should return "Hello World"',(done) => {
http.get('http://127.0.0.1:3000',(res) => {
expect(res).to.contain('wtf world')
done()
})
})
})
the above doesnt seem to work. the server doesnt fully load in the before block despite providing the done() call as well.
edit - i've tried splitting the server.js file to invoke a different server based on how its imported.
const main = () => {
console.log('main server')
new SocketCluster({
workers:1,
brokers:1,
port: 3000,
appName:null,
initController: __dirname + '/init.js',
workerController: __dirname + '/worker.js',
brokerController: __dirname + '/broker.js',
socketChannelLimit: 1000,
crashWorkerOnError: true
})
}
export const test = (port,done) => {
console.log('testing server')
new SocketCluster({
workers:1,
brokers:1,
port: port,
appName:null,
initController: __dirname + '/init.js',
workerController: __dirname + '/worker.js',
brokerController: __dirname + '/broker.js',
socketChannelLimit: 1000,
crashWorkerOnError: true
})
done()
}
if (require.main === module){
main()
}
and in test.js , i do this - still doesnt seem to work though
import {expect} from 'chai';
import {test} from '../../server/server'
describe('Express server',() =>{
before(function(done){
test(3000,done)
})
it('should return "Hello World"',(done) => {
http.get('http://127.0.0.1:3000',(res) => {
expect(res).to.contain('world')
done()
})
})
})
edit:2 - trie another way by returning a promise from the server.js file. still doesnt work
export const test = (port) => {
console.log('testing server')
return Promise.resolve(new SocketCluster({
workers:1,
brokers:1,
port: port,
appName:null,
initController: __dirname + '/init.js',
workerController: __dirname + '/worker.js',
brokerController: __dirname + '/broker.js',
socketChannelLimit: 1000,
crashWorkerOnError: true
}))
}
and in the before hook
before(function(done,port){
test(3000).then(function(){
console.log('arguments: ',arguments)
done()
})
})
Your server
module doesn't have a callback, so it could not be ready when you call done()
in your beforeEach
method.
First, export your app
in your server
module.
Then, do something like:
const app = require('../../server/server').app;
let server;
before(done => {
server = app.listen(3000, done);
});
/**
...
your tests here
...
**/
/** and, if you need to close the server after the test **/
after(done => {
server.close(done);
});
This way, done()
will be called in the listen
callback, so in your tests the server will be listening correctly. Then, remember to close it after tests end (useful if server is required in one or more test suites).
The solution explained here worked for me, in particular:
At the end of server.js ( or app.js ):
app.listen( port, ip, function()
{
console.log( 'Server running on http://%s:%s', ip, port )
app.emit( "app_started" )
})
module.exports = app
and in test.js:
var server = require( '../server' )
before( done =>
{
server.on( "app_started", function()
{
done()
})
})
In this case, app sends an "app_started" event when it is listening, and the test code waits for it. The provided URL contains more details.
Hope it helps !
You need to wait until the server actually listens on the given port. This could be accomplished by exporting some kind of init function in your server.js, which takes the done callback from mocha.
In your server.js
let initCallback;
[..]
app.listen(port, function() {
if (initCallback) {
initCallback();
}
});
exports = {
init: function(cb) {
initCallback = cb;
}
}
In your test
beforeEach((done) => {
require('../../server/server').init(done)
})
Also see: How to know when node.js express server is up and ready to use
I combined the first two posts and it worked for mine. First, make sure you have init code in your app.js or server.js
// put this in the beginning of your app.js/server.js
let initCallback;
//put this in the end of your app.js/server.js
if (initCallback) {
// if this file was called with init function then initCallback will be used as call back for listen
app.listen(app.get('port'),"0.0.0.0",(initCallback)=>{
console.log("Server started on port "+app.get('port'));
});
}
else{
// if this file was not called with init function then we dont need call back for listen
app.listen(app.get('port'),"0.0.0.0",()=>{
console.log("Server started on port "+app.get('port'));
});
}
//now export the init function so initCallback can be changed to something else when called "init"
module.exports = {
init: function(cb) {
initCallback = cb;
}
}
Next in your test.js you will need this
//beginning of your code
const app = require("../../server/server").app;
before(done => {
require("../app").init();
done();
});
//end of your code
after(done => {
done();
});
I am no expert in javascript but this works for me. Hope it helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With