'How to close Express server inside Jest afterAll hook

I am trying to write integration tests for my Express server using Jest. Since Jest runs tests in parallel (and I would like to avoid running tests in sequence using --runInBand), I am using the get-port library to find a random available port so that different test suites don't have port collisions.

My tests all run successfully, the only problem is that the server is failing to close down properly inside the afterAll hook. This causes Jest to print the following in the console...

Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't stopped in your tests.
Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.

When I use the --detectOpenHandles flag, Jest just hangs after tests complete. Nothing gets printed to the console.

Here is my test code...

let axios = require('axios')
const getPort = require('get-port')
const { app } = require('../../index')
const { Todo } = require('../../models')

// set reference to server to access
// from within afterAll hook
let server

beforeAll(async () => {
  const port = await getPort()
  axios = axios.create({ baseURL: `http://localhost:${port}` })
  server = app.listen(port)
})

afterAll(() => {
  server.close()
})

describe('GET /todos', () => {
  it('returns a list of todos', async () => {
    const { data: todos } = await axios.get('/todos')
    todos.forEach(todo => {
      expect(Todo.validate(todo)).toEqual(true)
    })
  })
})


Solution 1:[1]

I am on that github thread on this issue. Here is exactly the configuration that works for me. In package.json

"test": "jest --no-cache  --detectOpenHandles --runInBand --forceExit",

Here is the configuration in test file

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

afterAll(async () => {
  await new Promise(resolve => setTimeout(() => resolve(), 10000)); // avoid jest open handle error
});

beforeEach(() => {
  // eslint-disable-next-line global-require
  server = require('../index');
  jest.setTimeout(30000);
});

OR you have only afterAll to set timeout and settimeout for each test in the test body individually.That's example below

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

afterAll(async () => {
  await new Promise(resolve => setTimeout(() => resolve(), 10000)); // avoid jest open handle error
});

beforeEach(() => {
  // eslint-disable-next-line global-require
  server = require('../index');
});


describe('POST /customers', () => {
  jest.setTimeout(30000);
  test('It creates a customer', async () => {
    const r = Math.random()
      .toString(36)
      .substring(7);
    const response = await request(server)
      .post('/customers')
      .query({
        name: r,
        email: `${r}@${r}.com`,
        password: 'beautiful',
      });
    // console.log(response.body);
    expect(response.body).toHaveProperty('customer');
    expect(response.body).toHaveProperty('accessToken');
    expect(response.statusCode).toBe(200);
  });
});

Solution 2:[2]

The root cause is that the express app server is still running after the tests complete. So the solution is to close the server.

In the main server file:

export const server = app.listen(...)

In the test file:

import { server } from './main-server-file'

afterAll(() => {
  server.close();
});

Using nodejs 17.4.0, jest 27.5.1, supertest 6.2.2. Running test with cross-env NODE_OPTIONS=--experimental-vm-modules NODE_ENV=test jest

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2