30

I'm creating a testing for my express app. The project has multiple test files. In each module the server instance is required at beforeEach() method and closed at afterEach() method. but after testing one or two of the modules it'll raise address already in use and jest won't terminate.

beforeEach(() =>  {

    server = require('./../../../bin/www')});
afterEach(async() => { 
    server.close();
    /**
     * database cleanup logic goes here.
     */

 });

I want jest to terminate after all the test suites are completed.

skyboyer
  • 22,209
  • 7
  • 57
  • 64
btinsae
  • 503
  • 2
  • 5
  • 13

10 Answers10

61

Jest runs test suites in parallel by default. However, the machine that the tests are running on only has a single port 3000 (or port 4000, etc.)

This means that if you tell your app to listen on a port, and then start multiple concurrent processes, there will be a port collision and Jest will exit with EADDRINUSE - port already in use.

Like some people already mentioned, you can solve the issue by running your tests in sequence with --runInBand. However, you will lose out on the performance benefits of parallelization.

There is another way... do not explicitly listen on a port when running tests.

if (process.env.NODE_ENV !== 'test') {
  app.listen(port, () => console.log(`Listening on port ${port}`)
}

Now, when you feed your app to supertest, it will run your app on port 0 since it's not already running on a port.

const app = require('../app')
const request = require('supertest')

it('gets todos', async () => {
  const response = await request(app).get('/todos')
  expect(response.body.length).toEqual(3)
}

What exactly is port 0 you ask? That is how you tell Unix machines...

Choose the first randomly available port that you find.

Now that each test suite is running on a randomly available port, there is no longer a risk of port collisions which means we've solved the EADDRINUSE - port already in use error and we can continue running tests in parallel.

J. Munson
  • 2,275
  • 2
  • 17
  • 22
  • 1
    How do you get the tests to know which PORT to connect to for their worker? Eg, once the server has been bound, how do we know what it was bound to? – fbartho Jan 13 '22 at 20:48
  • @fbartho, I have never actually needed to know which port supertest is running the app on. In my example above, supertest is able to communicate with the app and that's all I really care about. If you _really_ need to know which port though, you could try digging through the supertest docs or looking at their source code. – J. Munson Jan 15 '22 at 01:02
  • Worth mentioning that you got to set the environment variable in your test script `set NODE_ENV=test` – Ale Mar 24 '23 at 10:05
37

I had this issue, and seem to have solved it by setting jest to only use one worker. I think the issue was caused by more than one instance of the server running with the same port, thereby causing the clash.

This is the line from my npm package.json file. The --maxWorkers=1 seems to have done the trick (https://jestjs.io/docs/en/cli#maxworkers-num-string).

"scripts": {
  "test": "jest --forceExit --detectOpenHandles  --watchAll --maxWorkers=1"
},
John Forbes
  • 1,248
  • 14
  • 19
  • results in this error if you are using both runInBand and maxWorkers: `Both --runInBand and --maxWorkers were specified, but these two options do not make sense together. Which is it? ` – Sunil Kumar Feb 27 '21 at 19:00
15

the very basic thing to do is to put your app.listen() inside some other file , other than your file that consists all the routes . for example : index.js

const express = require("express");
const app = express();
const bodyParser = require("body-parser");

app.use(bodyParser.json());

const students = ["Elie", "Matt", "Joel", "Michael"];

app.get("/", (req, res) => {
  return res.json(students);
});

module.exports = app;

and server.js

const app = require("./index");

app.listen(3000, () => console.log("server starting on port 3000!"));

In this way Jest will not be reading app.listen() at all . cheers !

VicCoder0659
  • 173
  • 1
  • 7
5

I'm using, node express server 4.16.2. If you're using a different version this may not apply to you. In your describe block when you're closing the server, await for it.

describe('auth midleware', () => {
        // loading the server
        beforeEach(() => {>         
            server = require('../../../index')
        });

        // closing the server
        afterEach(async () => {
            await server.close();        
        });


        // the tests will go here
    });

Also after you do this, run your node application in the command line.

node index.js

Check your package.json, how your script test tag is named. Mine is test.

{ ...
      "scripts": {
        "test": "jest --verbose --coverage --forceExit --watchAll --maxWorkers=1"
      }
      ...
    }

After that run your test again.

npm test  
Sergiu Mare
  • 1,552
  • 15
  • 17
  • I am just curious, why do you run your tests in watch mode? – swateek Oct 17 '22 at 03:59
  • 1
    It's a way of seeing if the code is OK when you save. Also quite handy when you write TTD. – Sergiu Mare Oct 17 '22 at 10:14
  • You cannot run your tests in watch mode on a CI pipeline, correct? – swateek Oct 19 '22 at 04:00
  • 1
    I'm not aware to be fair, the tests which runs in CI pipeline is just some js in a node env. But the watch mode has to watch any sort of change over a file. The pipeline triggers once something happens (merge to development), why do you want to use the watch mode in the pipeline? – Sergiu Mare Oct 20 '22 at 09:26
3

There is also an option --runInBand to run tests one by one.

Dmitry Skryabin
  • 1,584
  • 2
  • 10
  • 15
2
server = require('./../../../bin/www')});
afterEach(async() => { 
   await server.close();

you just have make it await to close the server properly.

1

You can just set env variable PORT=0 in your package.json test command in scripts object like this and make sure that you allways read port from env variables.

package.json

"scripts": {
    "test": "set PORT=0&&jest --watchAll --verbose"
}

index.js

const port = process.env.PORT || 3000;
const server = app.listen(port, () =>
  winston.info(`listening on port ${port}`)
);

setting PORT to 0 makes sure that jest chooses random available PORT. and its better solution than setting --maxWorkers=1 (which means that 1 worker will process all the test files in "series" not in "parallel") that was mentioned above cause it ensures that tests are running in parallel which makes testing process faster.

xcalibur
  • 11
  • 2
1

The --maxWorkers=1 from the accepted answer tanks performance of our test. It will do them one at a time which can take minutes in larger apps, and worse, on only one thread.

The reason it is throwing the error is that everytime we call a new endpoint, we are creating a new server on port 3000. what if we didn't??

Instead we assign a random port number between 5000 and 6500 if we are running a test!

if(process.env.NODE_ENV == 'test') {
    port = Math.floor(Math.random()*60000)+5000;
}
miken32
  • 42,008
  • 16
  • 111
  • 154
Rahimi0151
  • 395
  • 3
  • 11
0

I had the same issue, it turns out the last test case was not allowing jest close properly before moving to the next test suite, so I added

it('should blah blah', asyn(done) => {
   test code...
   done()
})
Simson
  • 3,373
  • 2
  • 24
  • 38
Slim
  • 1
  • 2
0

I just had the same problem, for all those who read this, it can also happen when you forget to export your app.listen

tonga
  • 191
  • 3
  • 12