Aw3 Sol
Aw3 Sol

Reputation: 1343

Jest did not exit one second after the test run has completed using express

I'm using JEST for unit testing my express routes.

While running the yarn test all my test case are getting passed, but I'm getting an error

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.

I used async & done, but still it throws the above error.

Below is my spec code. Please help

routes.spec.ts

const request = require('supertest');
describe('Test the root path', () => {
  const app = require('./index');

  test('GET /gql/gql-communication-portal/release-notes', async (done) => {
    const response = await request(app).get('/gql/gql-communication-portal/release-notes');
    expect(response.status).toBe(200);
    done();
  });
});

Upvotes: 123

Views: 152894

Answers (17)

Precious
Precious

Reputation: 1

I had this same problem and in my case, i was using knex.js. what i did first was in index.ts, i added

const PORT = process.env.PORT || 3000;
if (process.env.NODE_ENV !== "test") {
  app.listen(PORT, () => console.log(`Listening on port ${PORT}`));
}

Then in my .test.ts file, just before i started running the test, i added

import request from "supertest";
import app from "@/index";
import { Server } from "http";
import db from "../../db";

let server: Server;

beforeAll((done) => {
  server = app.listen(process.env.PORT || 3000, () => {
    done();
  });
});

afterAll((done) => {
  db.destroy(); // This part does the trick and closes the db connection
  server.close(done);
});

db being the knex.js configuration.

Upvotes: 0

Jesse Yentsch
Jesse Yentsch

Reputation: 218

For those experiencing this when working with Firebase, you likely have to call testEnv.cleanup()

let testEnv;

beforeAll(async () => {
  // Silence expected rules rejections from Firestore SDK. Unexpected rejections
  // will still bubble up and will be thrown as an error (failing the tests).
  setLogLevel("error");
  testEnv = await initializeTestEnvironment({
    projectId: projectId,
    firestore: {
      rules: readFileSync("firestore.rules", "utf8"),
      host: "127.0.0.1",
      port: 8080,
    },
  });
});


afterAll(async () => {
    await testEnv.cleanup();
})

Upvotes: 0

Mehmet Eren Yener
Mehmet Eren Yener

Reputation: 2036

This is usually happens when there are some async operations that have not finished by the the time jest is trying to exit.

in app.js export app and server

const express = require('express');
const app = express();


const server = app.listen(3000, () => {
  console.log('App is listening on port 3000');
});


module.exports = { app, server };

in test.js

const request = require('supertest');
const { app, server } = require('../app');

describe('Monitoring Routes', () => {

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

    it('should respond with status 200 for /path1/path2', async () => {
        const response = await request(app).get('/path1/path2');
        expect(response.status).toBe(200);
    });


});

Upvotes: 3

Black Mamba
Black Mamba

Reputation: 15545

For me it was a different issue I was using supertest to test routes itself so I had to close the connection to the server itself.

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

You can start server in beforeAll block and close in after all:

beforeAll(() => {
   server = app.listen(someRandomNumberHere); // Random number is needed to avoid using same port in different tests if you run in parallel
})

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

Additional details in case not clear:

const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
  res.send('Hello World!');
});
module.exports = app;

And in index.js or which ever you use to boot up import this something like this:

const app = require('./app'); // assuming app.js is in the same directory

If this is not the case for you this issue might have something for you

Upvotes: 26

Nermin
Nermin

Reputation: 935

2023 clean & simple oneliner

Inspiration from Saleh's answer but you don't have to use done.

Put this at the end of the test file:

afterAll(() => mongoose.connection.close())

Upvotes: 3

Isuru Maldeniya
Isuru Maldeniya

Reputation: 348

I got the same problem due to not closing the database connection after all the test cases runs. It can be done using jest.config.js and adding a new file to root folder which will globally close the database connection after all the test cases.

In jest.config.js file add the following to the module.exports ,

module.exports = {
   setupFilesAfterEnv: ['<rootDir>/src/runAfterAllTests.ts'],
}

Then add runAfterAllTests.ts to src folder.

runAfterAllTests.ts will globally close the db connection. For mongodb it should be something like this.

import { client } from './database/connect';

global.afterAll(async () => {
  await client.close();
});

This will close the db connection after all the tests and fix the issue.

Upvotes: 2

Tri Dawn
Tri Dawn

Reputation: 780

My problem is that I use Sequilize ORM and I forgot to close it at the end of testing inside the afterAll function callback.

This is my code:

afterAll((done) => {
    MatrixTableHeaderCol.destroy({
        where: {},
        force: true
    }).then(() => {
        done();
    });
});

Upvotes: 0

Obinna Nnenanya
Obinna Nnenanya

Reputation: 1818

In NestJs apps, where you have something like

app = moduleFixture.createNestApplication();
await app.init();

Just add this in your test file

afterAll(done => {
    app.close()
    done()
})

You can as well use async

afterAll(async() => {
    await app.close()
})

Upvotes: 4

Toomuchrice4u
Toomuchrice4u

Reputation: 628

I was running into the same issue but, I was using the pg module with my NodeJS Express app. I want to post this for those using this stack as well if it helps them.

Essentially, supertest creates a server connection, which some people might get the TCPSERVERWRAP error because it doesn't get closed, regardless whether I use async/await or the jest done callback. So, this has to be closed after each test. On top of that, the database connection remains open so I mocked it.

Closing the server connection and mocking pg together solved the error for me.

products.tests.ts

import request from 'supertest'
import { Pool } from 'pg'
import app from '../app'
import type { Server } from 'http'

jest.mock('pg')

const { ROUTE_VERSION = '/v1' } = process.env
const route = (path: string) => [ROUTE_VERSION, path].join('')
const pool = new Pool()
let server: Server

beforeEach(() => {
   server = app.listen(4000)
})

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

describe('GET /products', () => {
   it('returns array of products', async () => {
      await request(server)
         .get(route('/products'))
         .expect(200)
         .expect((res) => {
            expect(pool.query).toBeCalledTimes(1)
            expect(res.body).toBeInstanceOf(Array)
            expect(res.body).not.toHaveLength(0)
         })
   })
})

UPDATE: Meant to use beforeEach and afterEach to close the server after EACH test, otherwise, it still remains open like before.

UPDATE 2: Using async/await otherwise, it will always pass because request is asynchronous and doesn't complete unless you wait for it to finish.

Upvotes: 2

shrikanta mazumder
shrikanta mazumder

Reputation: 373

You can try this one

"test": "jest --runInBand --force-exit"

Upvotes: 5

Sachitha Hirushan
Sachitha Hirushan

Reputation: 643

I have added this line to package.json

It worked for me

jest --runInBand --detectOpenHandles --forceExit

Upvotes: 39

aquinq
aquinq

Reputation: 1448

Adding

jest.useFakeTimers();

at the beginning of the test suite fixed it for me.

Might come from timers defined in components part of the render (like throttled buttons, mocks etc..).

Upvotes: 17

Umair A.
Umair A.

Reputation: 6873

For Firebase I had to call cleanup()

import {
    assertFails,
    assertSucceeds,
    initializeTestEnvironment,
    RulesTestEnvironment,
} from "@firebase/rules-unit-testing";
import { doc, setDoc } from "firebase/firestore";

it('creates a new user document in firebase', async () => {
    const testEnv = await initializeTestEnvironment({
        projectId: "appname-test",
        firestore: {
            host: 'localhost',
            port: 8088
        }
    });

    const alice = testEnv.authenticatedContext("alice");

    await assertSucceeds(setDoc(doc(alice.firestore(), "users", "alice"), {
        fname: "Alice",
        lname: "Wonderland",
        dob: "18/01/1999",
        email: "[email protected]"
    }));

    return await testEnv.cleanup();
});

Upvotes: 4

olawalejuwonm
olawalejuwonm

Reputation: 1527

This worked for me

const mongoose = require('mongoose');
    afterAll(async(done) => {
  // Closing the DB connection allows Jest to exit successfully.
  try {
    await mongoose.connection.close();
    done()
  } catch (error) {
    console.log(error);
    done()
  }
  // done()
})

Upvotes: 10

Guillaume
Guillaume

Reputation: 391

On my side, I just separate app.listen() from my app. So with express, your app finish with an export.

// index.js
module.exports = app;

And just create another file to listen the port.

// server.js
const app = require('./index')
app.listen(...)

And if you import just the index (app index.js) in your tests, it should work with no extra config. Of course your need to adjust the start of your express app. It should use now server.js.

Upvotes: 16

Saleh Rahimzadeh
Saleh Rahimzadeh

Reputation: 1361

My problem was solved by this code:

beforeAll(done => {
  done()
})

afterAll(done => {
  // Closing the DB connection allows Jest to exit successfully.
  mongoose.connection.close()
  done()
})

Upvotes: 101

user9996891
user9996891

Reputation:

I was having the same issue but in my package.json file i added "test": "jest --detectOpenHandles" and ran npm test --detectOpenHandles. I didn't get the error message this time. Maybe you can try doing that.

Upvotes: 15

Related Questions