7

I am looking for a correct way to completely reset my express server between tests! It seems that this is not just a problem for me, many other users have asked the same question, and many blog posts had been written on the argument. The proposed solutions are not working nor satisfactory for me. Here there is another similar question and an article that well describes the problem and suggests some solutions:

Stack overflow Closing an express server after running jasmine specs

Blog: https://glebbahmutov.com/blog/how-to-correctly-unit-test-express-server/

Here a package is specifically created to solve this problem: https://www.npmjs.com/package/server-destroy

Now, a minimal working example to reproduce my condition. In the code under test, an express server is created; when called on some endpoint, the server increments a value and returns it:

( function() {
   'use strict'

   const enableDestroy = require( 'server-destroy' )
   const app = require( 'express' )()
   const http = require( 'http' )

   let val = 0

   app.use( '/inc', (req, res) => {
      val ++
      res.send(val.toString())
   } )

   const server = http.createServer( app )

   server.listen( 3000 )

   enableDestroy(server);

   module.exports = server

} )()

The test consists in two identical test cases; both of them call the server on the endpoint, and check the returned value. before_each and after_each sections are provided in order to assure a new connection is created before the single test case is run, and then closed, to assure independence between the two test cases:

const chai = require( 'chai' )
const chaiHttp = require( 'chai-http' )
const expect = chai.expect
chai.use( chaiHttp )

let server

describe( 'first test group', () => {
   beforeEach( () => {
      server = require( './server' )
   } ),

   afterEach( ( done ) => {
      server.destroy( done )
      delete require.cache[require.resolve( './server' )]
   } ),

   it( 'should respond 1', ( done ) => {
      chai.request( server )
         .get( '/inc' )
         .set( 'Connection', 'close' )
         .end( ( err, res ) => {
            expect( res.text ).to.be.equal( '1' )
            done()
         } )
   } ),

   it( 'should respond 1', ( done ) => {
      chai.request( server )
         .get( '/inc' )
         .set( 'Connection', 'close' )
         .end( ( err, res ) => {
            expect( res.text ).to.be.equal( '1' )
            done()
         } )
   } )
} )

The test fails because the server is not running after the first test. Please note that in the after_each section I forced a cache cleaning for lost completely the last server instance. The test succeeds if a single test case is run:

first test group
    ✓ should respond 1
    1) "after each" hook for "should respond 1"


  1 passing (70ms)
  1 failing

  1) first test group
       "after each" hook for "should respond 1":
     Error: Not running
      at Server.close (net.js:1620:12)
      at emitCloseNT (net.js:1671:8)
      at _combinedTickCallback (internal/process/next_tick.js:135:11)
      at process._tickCallback (internal/process/next_tick.js:180:9)

The configuration I used:

  • Node 8.11.1
  • Express: "4.16.3"
  • Mocha 5.0.5
  • chai 4.1.2
  • chai-http 4.0.0

How can I solve this problem? And what does the error message mean?

Elia
  • 1,417
  • 1
  • 18
  • 28
  • 1
    Usually, testing is done with a web server proxy and not with a server. Is there any reason that you cannot use a proxy for `express` like `supertest`? – user2347763 Apr 12 '18 at 09:37
  • I think that I'm using a proxy, in my case should be `chai-http` isn't it? – Elia Apr 12 '18 at 10:13
  • In afterEach, you are destroying the server and quite possible that before the server starts again, the second test is run. 1. Try using a timeout before running the second test. 2. Use keepOpen as in const chaiHttp = chai.request(app).keepOpen() (this line is from the chai-http docs) to keep the server open. In any case, remove both the afterEach block and the .set('connection',close) line, as they're not doing anything useful for your tests – user2347763 Apr 12 '18 at 19:48

2 Answers2

8

It works with a little modification I didn't use server-destroy since server.close works just fine

server.js

// ( function() { // no need for this
   'use strict'

   //const enableDestroy = require( 'server-destroy' )
   const app = require( 'express' )()
   const http = require( 'http' )

   let val = 0

   app.use( '/inc', (req, res) => {
      val ++
      res.send(val.toString())
   } )

   const server = http.createServer( app )

   server.listen( 3000 )

   // enableDestroy(server);    
   module.exports = server

// } )()

test.js

const chai = require( 'chai' )
const chaiHttp = require( 'chai-http' )
const expect = chai.expect
chai.use( chaiHttp )

let server

describe( 'first test group', () => {
   beforeEach( () => {
      server = require( './server' )
   } ),

   afterEach( ( done ) => {

      // UPDATE DON'T CLOSE THE SERVER

      delete require.cache[require.resolve( './server' )]
      done()

      //server.close( () => {
      //   delete require.cache[require.resolve( './server' )]
      //   done()
      //})      
   } ),

   it( 'should respond 1', ( done ) => {
      chai.request( server )
         .get( '/inc' )
         .set( 'Connection', 'close' )
         .end( ( err, res ) => {
            expect( res.text ).to.be.equal( '1' )
            done()
         } )
   } ),

   it( 'should respond 1', ( done ) => {
      chai.request( server )
         .get( '/inc' )
         .set( 'Connection', 'close' )
         .end( ( err, res ) => {
            expect( res.text ).to.be.equal( '1' )
            done()
         } )
   } )
} )
Molda
  • 5,619
  • 2
  • 23
  • 39
  • Thank you! This work I give you an up-vote but I think that can't be the right solution: the cb you passed to close ignores the arguments (the error in this case). You can check simply logging the argument passed to the close cb: the error is still present here! `server.close( (arg) => { console.log( arg ) delete require.cache[require.resolve( './server' )] done() })` – Elia Apr 12 '18 at 10:06
  • 1
    You are right, so i updated my code. Don't call `server.close`. The server will be closed automaticly, i don't know why though. You can check that it's closing by adding this line `server.on( 'close', () => console.log('Closing') )` just bellow `server.listen` in server.js file. – Molda Apr 12 '18 at 11:21
  • Yes, It's right! Seems that someone close the server for me, so there is no more the necessity to manually close the server but remain the constraints to delete the cache if you want refresh the server instance! So If you update your response with this news I will accept it! – Elia Apr 12 '18 at 12:14
1

this is after some time from the initial question.

On the chai-http documentation i could able to find that it has mechanism to open up the server connection using the keepOpen() method from the request method. (actually it returns back an agent)

const chai = require("chai");
const chaiHttp = require("chai-http");

chai.use(chaiHttp);

const chaiAppServer = chai.request(server).keepOpen();

https://www.chaijs.com/plugins/chai-http/#integration-testing

But as it mentions, we have to manually close the server connection.

describe("TEST Case group", () => {

    after(() => {
        chaiAppServer.close();
    });

    //..... integration test cases

});