Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ensuring server app runs before mocha tests start

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()
    })
  })
like image 557
Kannaj Avatar asked Jul 06 '16 11:07

Kannaj


4 Answers

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).

like image 71
xmikex83 Avatar answered Oct 18 '22 01:10

xmikex83


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 !

like image 26
santiago Avatar answered Oct 18 '22 02:10

santiago


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

like image 2
scthi Avatar answered Oct 18 '22 01:10

scthi


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!

like image 1
liu liu Avatar answered Oct 18 '22 03:10

liu liu