Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jest did not exit one second after the test run has completed. #7287

Open
jamalsoueidan opened this issue Oct 26, 2018 · 51 comments
Open

Jest did not exit one second after the test run has completed. #7287

jamalsoueidan opened this issue Oct 26, 2018 · 51 comments

Comments

@jamalsoueidan
Copy link

jamalsoueidan commented Oct 26, 2018

I'm getting this message every time i'm using any libraries that depend on promises.

🐛 Bug Report

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.

To Reproduce

I have a function that need to make a request to external api, and in the same method just save in the database without waiting for a response.

I don't want to wait until the saving process is done, but i'm forced to change the behaviour of my application to get it tested through jest., or i need to close the connection, stop the server for my code to work.

Expected behavior

Excecpted jest to stop and return to my console.

Link to repl or repo (highly encouraged)

line49 and line50

test("it should create new order", async () => {
  const response = await server.inject({
    method: "POST",
    url: "/api/orders",
    payload: JSON.stringify({
      customer: {
        email: "asd@gmail.com",
        phone: "20 51 75 95",
        city: "Aarhus",
        zip: "8000",
        first_name: "jamal",
        last_name: "soueidan"
      },
      properties: [
        {
          name: "custom engraving",
          value: "Happy Birthday Mom!"
        }
      ]
    })
  });

  expect(response.statusCode).toBe(200);
});

I had to make those changes to get jest working with my api server and mongodb.
jamalsoueidan/giv-et-tilbud@d8f326b

Run npx envinfo --preset jest

  System:
    OS: Windows 10
    CPU: x64 Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz
  Binaries:
    npm: 6.4.1 - C:\Program Files\nodejs\npm.CMD
@jamalsoueidan jamalsoueidan changed the title Async Jest did not exit one second after the test run has completed. Oct 26, 2018
@ExoMemphiz
Copy link

ExoMemphiz commented Oct 27, 2018

Try putting "done" inside the callback from the test function as so:

const whateverYouExpect = 123;
test('some description of test', async (done) => {  
    await someAsyncTask();  
    await secondAsyncTask();  
    evenCallbacksCanWork((result) => {  
        expect(result).toBe(whateverYouExpect);  
        done();
    });  
})

Additionally, if a connection is being kept open, this will also stop the test from completing, so closing any open connection would be recommended as well.

@jamalsoueidan
Copy link
Author

Thank you ExoMemphiz, but this callback, from where should I reachit?

It's too deep inside the server.

I moved to Ava, it works out of the box 👍

@jamalsoueidan
Copy link
Author

Well well, my danish brother :)

The problem is that I'm using mongoose to save something async when I call this api, and I cannot reach this code to figure out when it's finished saving the document inside mongo.

await server.inject

The code above actually wait for the response only, and it works, but jest complain about the mongoose save, it still in progress saving behind the scene.

I tried to disconnect mongoose, and close each connection, but it doesn't work.

@dhurlburtusa
Copy link

dhurlburtusa commented Oct 29, 2018

I was getting the same error in one of my integration tests. In it, I was using a Sequelize Model to setup the database to a known state in beforeAll or beforeEach. The fix was to call close on the Sequelize instance connected to the Model in the afterAll callback. Hope this helps someone else.

EDITED:

And by popular request, here is effectively what I've done:

# dbConnection.js
export default new Sequelize({...}); // The Sequelize instance.
# some.spec.js
import dbConnection from './dbConnection';

const { SomeModel } = dbConnection.models;

describe('...', () => {
  beforeEach(async () => {
      await SomeModel.create({...});
  });
  ...
});

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

@Shrikant9
@AnitaMartinez

I added an example.

@Shrikant9
Copy link

I was getting the same error in one of my integration tests. In it, I was using a Sequelize Model to setup the database to a known state in beforeAll. The fix was to call close on the Sequelize instance connected to the Model in the afterAll callback. Hope this helps someone else.

What version of Sequelize are you using? I get sequelizeInstance.close() is not a function.

@AnitaMartinez
Copy link

I was getting the same error in one of my integration tests. In it, I was using a Sequelize Model to setup the database to a known state in beforeAll. The fix was to call close on the Sequelize instance connected to the Model in the afterAll callback. Hope this helps someone else.

Can you show your code?

@jabolina
Copy link

jabolina commented Jan 19, 2019

Hi, I was getting the same error, with @dhurlburtusa tip, my code was @AnitaMartinez, I'm new in TypeScript, so I'm pretty sure this can be done in a better way:

let conn: Connection;
const createGlobalDatabaseConnection = (fn: Function) => dbConnection().then((conn: Connection) => fn(conn));

createGlobalDatabaseConnection((connection: Connection) => {
    conn = connection;
});

const closeGlobalDatabaseConnection = async () => {
    await conn.close();
}

afterAll(async () => {
    await closeGlobalDatabaseConnection();
});

The dbConnection Promise is the function that actually connects to the database instance.

@ErfanSeidipoor
Copy link

I was getting the same error in one of my integration tests. In it, I use mongo. The fix was to call close on the mongoose.connection . Hope this helps someone else.

const mongoose = require('mongoose')
describe(' ...... ', ()=>{
  afterAll( async () =>{
        await mongoose.connection.close()
    })
})

@chaseholdren
Copy link

Had this problem with tests that used firebase. This fixed it:

beforeAll(async () => {
  await firebase.firestore().enableNetwork();
});

afterAll(async () => {
  await firebase.firestore().disableNetwork();
});

@divyanshu013
Copy link

If you've multiple test files, I use global setup and teardown. For example in jest.config.js I defined:

module.exports = {
    globalSetup: './setupTests.js',
    globalTeardown: './teardownTests.js',
};

In setupTests I connect to the db and return a function which returns a promise. Similarly in teardownTests I close the db connection and return a function which returns a promise.

@jhonnymoreira
Copy link

I was having this problem with Knex and using global setup and teardown files on Jest.

My solution was:

// setup.js
const knexConfiguration = require('../config/knex')[process.env.NODE_ENV];
const knex = require('knex');

const setup = async () => {
  const client = knex(knexConfiguration)
  await client.migrate.latest();
  await client.destroy();
};

module.exports = setup;
// teardown.js
const knexConfiguration = require('../config/knex')[process.env.NODE_ENV];
const knex = require('knex');

const teardown = async () => {
  const client = knex(knexConfiguration)
  await client.migrate.rollback();
  await client.destroy();
};

module.exports = teardown;

Hope it helps anybody facing this kind of problem.

@lucasfcosta
Copy link
Contributor

Hello everyone,

Please correct me if I'm wrong but this seems like an issue which is not on Jest's side.

If you want the tests to exit even when there are external resources being held I think you should use --forceExit. The forceExit flag is specifically designed for this case and therefore is an indication that tests not exiting when it's not present is indeed an expected behaviour and not a bug.

Force Jest to exit after all tests have completed running. This is useful when resources set up by test code cannot be adequately cleaned up. Note: This feature is an escape-hatch. If Jest doesn't exit at the end of a test run, it means external resources are still being held on to or timers are still pending in your code. It is advised to tear down external resources after each test to make sure Jest can shut down cleanly.

As the author mentioned, it's possible to use --detectOpenHandles for debugging this (and this is also mentioned in the docs btw).

Given the arguments above, perhaps this issue could be closed?

@nathanloyer
Copy link

@lucasfcosta Every time I've tried using --detectOpenHandles in my test code in a few different applications over the years it has never provided me with any useful output. It just sits there waiting for everything to wrap up but without printing the warning that suggests to use the argument in the first place. Is there some trick to getting useful output from that argument that I am unaware of?

@rohitkrishna094
Copy link

Is there any other way to debug? --detectOpenHandles is not showing any output. Thanks

@bennycode
Copy link

Same here. I get no output from --detectOpenHandles.

$ yarn test:types && jest --config ../../jest.config.js --detectOpenHandles
$ tsc --noEmit
 PASS  src/App.test.tsx (9.447s)
  √ renders without crashing (240ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        10.201s, estimated 14s
Ran all test suites.
(node:9784) UnhandledPromiseRejectionWarning: Error: connect ECONNREFUSED 127.0.0.1:80
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1126:14)
(node:9784) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a prom
ise which was not handled with .catch(). (rejection id: 2)
(node:9784) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-z
ero exit code.

My jest.config.js:

module.exports = {
  bail: true,
  collectCoverage: false,
  collectCoverageFrom: ['src/**/*.{ts,tsx}', '!src/index.tsx', '!src/main.ts', '!**/node_modules/**'],
  coverageDirectory: '<rootDir>/coverage',
  coverageThreshold: {
    global: {
      branches: 100,
      functions: 100,
      lines: 100,
      statements: 100,
    },
  },
  moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx'],
  rootDir: process.cwd(),
  testMatch: ['<rootDir>/src/**/*.test.{ts,tsx}'],
  transform: {
    '^.+\\.tsx?$': 'ts-jest',
  },
  verbose: true,
};

My test case:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App />, div);
  ReactDOM.unmountComponentAtNode(div);
});

@aecorredor
Copy link

For anyone running into this using sequelize, I had to do this:

  afterAll(async done => {
    await models.sequelize.close();
    done();
  });

That fixed the issue.

@khoahuynhdev
Copy link

I'm testing Jest with mongoose and supertest, add testEnvironment: 'node' fixed this issue for me.

@vgorloff
Copy link

Another possible reason I faced – not closed Redis connection.

afterAll(async done => {
   globalRedisClient.unref();
   done();
});

@pocketcolin
Copy link

My solution won't work for everyone, but if you scoured the web for as long as I did trying to figure out how to close that damn handle that was left open, this might help. In my ecosystem, we are running a Parse Server as middleware with an Express server, but this could apply to anyone who needs to run a separate server with the tests but is unable to close some socket or port at the end. I believe the issue causing the dreaded "Jest did not exit one second..." error for us was a result of Parse's MongoDB adapter not closing correctly.

So to get around this issue, I abstracted the server initialization completely out of the jest test suite and created a special npm script to run it in an entirely separate process.

NPM Script (package.json):

"scripts": {
    "test": "npm start & ./listen_on_port_5000.sh && jest test && kill $(lsof -t -i tcp:5000)",
}

listen_on_port_5000.sh

while ! nc -z localhost 5000
do
    sleep 0.5
done

So how does this work?

  1. npm start runs which is the normal command we would run to start our server with Express and Parse.
  2. Because we know that the server always starts on port 5000, we run the start script in the background and start a separate process running a shell script (listen_on_port_5000.sh) to wait for the server to boot up.
  3. Once the listener script detects anything running on port 5000, the npm script moves onto the jest command (all while the Express server is up and running).
  4. When Jest finishes running, the final script runs a kill script to close the server running on port 5000.

@jhenson29
Copy link

jhenson29 commented Jan 11, 2020

I had tests that were checking auto generated http server and socket.io connections.
The tests were running fine on my windows machine, but failing for not exiting on my Jenkins server.

I was closing both the socket.io server and http server correctly at the end of the test, but I was not closing the clients. Once I added the cleanup for the clients, the tests exited successfully on their own on both my dev machine and CI server.

The tests would work without closing the clients if I used --forceExit, but at the end of the day, the real problem was improper cleanup.

I also found that --detectOpenHandles did not produce any output.

@jeremygottfried
Copy link

I have run into the same issue with globalSetup and globalTeardown hooks, but I can't find any processes left unattended. I added done() to the end of all the tests, and I am stopping my server during teardown.

jest --runInBand --detectOpenHandles doesn't print anything to the console.

@sanket-work
Copy link

I have run into the same issue with globalSetup and globalTeardown hooks, but I can't find any processes left unattended. I added done() to the end of all the tests, and I am stopping my server during teardown.

jest --runInBand --detectOpenHandles doesn't print anything to the console.

Facing same issue tried closing sequelize connection , no luck there.
Even though the issue might not be with jest but its main problem is it doesn't display which processes are active --detectOpenHandles doesn't print anything 😕
On the top if we use --forceExit it shows message to use --detectOpenHandles
Irony 👎

@BigsonLvrocha
Copy link

BigsonLvrocha commented Jun 24, 2020

I've been having this problem too

I ended up using https://www.npmjs.com/package/why-is-node-running logging in beforeAll

import log from 'why-is-node-running';

afterAll(async () => {
  //normal cleanup things
  await new Promise(resolve => {
    setTimeout(() => {
      log()
      resolve()
    }, 4000)
  })
}

turns out that since I was using jest.resetModules, I had to reimport the module that was using a pg connection and close it there too.

@jamiehaywood
Copy link

jamiehaywood commented Jul 7, 2020

I'm also having this problem. To give some context, I am using SuperTest to do some e2e testing with MongoMemoryServer.
My setup and teardown looks like this:

let mongoServer;

beforeAll(async (done) => {
  mongoServer = new MongoMemoryServer({ instance: { port: 26016 } });
  const uri = await mongoServer.getUri();
  process.env.MONGODB_URI = uri;
  done();
});

afterAll(async (done) => {
  await mongoServer.stop();
  done();
}); 

Nothing out of the ordinary that would suggest that I'm following the documentation wrong.
I started troubleshooting by looking at Activity Monitor on MacOS. It seems that there are two node processes that are still running whilst jest is running.
image
However, as soon as I cancel / quit the test run, the processes also quit.

I'm going to investigate further by sampling the node processes and see exactly what spawns them. I will update here with my findings.

EDIT: Related issue #1456 - it looks like it's an underlying NodeJS issue. tl;dr - add the --forceExit flag to your test script in package.json

@dougecomp
Copy link

dougecomp commented Nov 17, 2020

I am having this error on several jest tests. I am closing express server properly with server.close().

On a test i solved the problem closing a mongoose connection properly using connection.disconnect().

But other tests are presenting the message yet. I noticed that all my tests that uses the supertest package keeps the message.

The way is to analyze case by case.

@younes-io
Copy link

What if I'm not calling a database ? What if I'm calling an external resource of which I have no control ? How do I "close" ?

@ajwad-shaikh
Copy link

@YounesTea you might have an async/await call in your code.
Try calling done() after you're, well, done 😉

it("Should test something", async (done) => {
  // Sends request...
  const req = await request(app).get("/someEndpoint");
  expect(req).toBeTruthy();
  done();
});

@dougecomp
Copy link

@YounesTea you might have an async/await call in your code.
Try calling done() after you're, well, done

it("Should test something", async (done) => {
  // Sends request...
  const req = await request(app).get("/someEndpoint");
  expect(req).toBeTruthy();
  done();
});

I have async/awaits and i am already using the done() callback on the tests including beforeAll and afterAll.
The problem persists...

@agordeev
Copy link

beforeAll(async () => {
await firebase.firestore().enableNetwork();
});

afterAll(async () => {
await firebase.firestore().disableNetwork();
});

That doesn't work. However, this one works:

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

@olawalejuwonm
Copy link

olawalejuwonm commented Nov 29, 2020

in mongo i solved this:

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

@golledge65
Copy link

This solution appears to be somewhat generic. We're using a postgres conn pool and starting to use Jest. I did this

afterAll(async () => {
pgPool.end();
});

Kind of obvious after you realize that the DAO modules are using a shared connection pool, and only the service itself should end() the pool.

@BigsonLvrocha
Copy link

This solution appears to be somewhat generic. We're using a postgres conn pool and starting to use Jest. I did this

afterAll(async () => {
pgPool.end();
});

Kind of obvious after you realize that the DAO modules are using a shared connection pool, and only the service itself should end() the pool.

The solution is generic indeed, each one has it's cases, the problema is - -detectOpenHandle outputting nothing

@jeremygottfried
Copy link

This is what ended up working for me (most of the time). I was running an API server and doing end to end testing with puppeteer. I needed a kill -9 command to end my API server. It might be because my server included child processes of its own that weren't killed with the normal command. So you need to kill all the child processes if you are running a complex server.

  await global.server.destroy();

  spawnSync('kill', ['-9', global.server.pid], { shell: true, cwd: cwd() });

That being said, --detectOpenHandle is def broken.

@moisesxz1
Copy link

globalRedisClient.unref();

Hey, i think i'm having the same issue, im currently trying to test a node express app using typescript and for testing i'm using jest and supertest and im getting the same errors, could you tell me how can i close the redis connection? tried importing redis and redisClient but couldnt find the unref() method

@trsp400
Copy link

trsp400 commented Feb 6, 2021

I was getting the same error in one of my integration tests. In it, I was using a Sequelize Model to setup the database to a known state in beforeAll or beforeEach. The fix was to call close on the Sequelize instance connected to the Model in the afterAll callback. Hope this helps someone else.

EDITED:

And by popular request, here is effectively what I've done:

# dbConnection.js
export default new Sequelize({...}); // The Sequelize instance.
# some.spec.js
import dbConnection from './dbConnection';

const { SomeModel } = dbConnection.models;

describe('...', () => {
  beforeEach(async () => {
      await SomeModel.create({...});
  });
  ...
});

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

@Shrikant9
@AnitaMartinez

I added an example.

Thansk!! It really makes sense that we have to close the connection after tests are done, you're awesome!

@Satys
Copy link

Satys commented Jul 16, 2021

After losing hope to detechOpenHandles and --runInBand and their combination, I have finally managed to solve it without using them.
I was having a similar problem while mocking ioredis.

Now, Previously I was simply mocking (jest.mock('../path/redis.ts')) the Redis client file (redis.ts) which was not working.

import IORedis from "ioredis";

const client = new IORedis({
    host: process.env.REDIS_HOST || "redis",
    port: Number(process.env.REDIS_PORT) || 6379
});

export default client;

To solve this, I have changed the way to mock the client library as per the requirement.
In a helper, I was using this library's hmset function, and for the unit test of the helper I needed to mock the same.

The following way of mocking has worked successfully.


jest.mock("../../src/config/redis", () => {
    return { hmset: jest.fn() };
});

You can include more functions (hdel, hmget, etc) of Redis client as per the requirement.

I hope this helps 🙏

@nathanloyer
Copy link

It's extremely disappointing that after almost 3 years there seems to have been zero effort to resolve this issue and make detection of unresolved promises any easier. Ideally node itself would make this an easier issue to debug. I don't know how many days I've lost over the years trying to debug these kinds of issues.

@dougecomp
Copy link

I have a similar problem with a database connection with knex. After call connection.destroy() on afterAll(), the message has gone.

@bitsnaps
Copy link

bitsnaps commented Sep 25, 2021

If you tried all different solutions above and none of them works for you, this probably because you're using an in-memory database, something like this:

var db = new sqlite3.Database(':memory:')

Which I don't know how to solve it either!
P.S. Running db.close() on process.exit() won't fix it.

@aquinq
Copy link

aquinq commented Oct 28, 2021

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..).

@ifunplus
Copy link

ifunplus commented Dec 9, 2021

--watchAll=false in ci environment

@pauldraper
Copy link

Node.js has an API process.exit() that jest could use.

Would be very helpful.

@github-actions
Copy link

This issue is stale because it has been open for 1 year with no activity. Remove stale label or comment or this will be closed in 30 days.

@github-actions github-actions bot added the Stale label Jan 19, 2023
@jeremygottfried
Copy link

AFAIK this hasn't been fixed

@github-actions github-actions bot removed the Stale label Jan 19, 2023
@jamalsoueidan
Copy link
Author

This is the solution for the problem.
https://medium.com/@jamalsoueidan/jest-connection-pool-closed-104b61578de9

@josefkim
Copy link

I was able to fix the issue for me in this way. 🥳

I first ran my test package.json script with the additional command of ps -e | grep node.
Like this: $ yarn run test && ps -e | grep node

So I could see which node processes are running after jest process has been finished.
In my case two intellij language node processes and my microsoft teams desktop client were running.
(remark: microsoft teams desktop client is running as a react native app on node js on your local machine)

To verify if my assumption is correct I closed intelliJ and my MS Teams Client and started the same script in my terminal.
It worked fine. I didnt get the warning anymore.

To be double assure I startet my MS Teams Client again and startet the test script in my terminal again.
I got the warning again.

Conclusion:
It seems that jest is considering other node processes as application processes. This shouldn't be the case.
I hope this helps to give a hint to fix this issue :)

gzamaury added a commit to gzamaury/fcc-exercisetracker that referenced this issue Jul 4, 2023
@char26
Copy link

char26 commented Nov 19, 2023

Fixed for me by closing my server and puppeteer browser before running the expect tests.
This did not close after tests completed:

expect(messages).toMatch(expectedMessages);
await server.close();
await browser.close();

This worked:

await server.close();
await browser.close();
expect(messages).toMatch(expectedMessages);

Seems like Jest got stuck after a failed test and never got to server.close().

@Jesse-Yentsch
Copy link

For those experiencing this when working with Firebase, you likely need 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();
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests