Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to correctly close express server between tests using mocha and chai

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?

like image 946
Elia Avatar asked Apr 12 '18 06:04

Elia


2 Answers

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()
         } )
   } )
} )
like image 122
Molda Avatar answered Sep 28 '22 08:09

Molda


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

});
like image 38
Jayanga Jayathilake Avatar answered Sep 28 '22 06:09

Jayanga Jayathilake