Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jest tests hang due to open Sequelize connections

The Setup

I have a NodeJS project that uses:

  • Jest for testing.
  • Sequelize as an ORM.

Sequelize is instantiated when loading the models module which gets imported by a few of the files that are being tested with Jest.

The Problem

Jest tests pass but then hang with the message:

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.

Note: Adding --detectOpenHandles to the test call does not affect the output.

I do not actually invoke the sequelize object from any test paths, however some of the tested files import the models module and therefore Sequelize is instantiated.

(I will also note that this only occurs on my TravisCI environment, but I suspect this is a red herring.)

The Context

Because Jest is running tests in parallel, the models module is loaded multiple times during the entire test process. I confirmed this with debug output saying SEQUELIZE LOADED which appears multiple times when I run the tests.

The Attempts

  1. I did attempt to invoke sequelize.close() inside of a globalTeardown but this appears to simply open (and then close) a new sequelize connection.

  2. Since none of the tests actually rely on a database connection, I attempted running sequelize.close() within the models module immediately before export. This fixed the issue (though obviously is not a solution).

  3. I have attempted to configure the test connection pools to aggressively end connections.

const sequelizeConfig = {
  ...
  pool: {
      idle: 0,
      evict: 0,
    }
}

This did nothing.

The Requirements

  1. I don't want to use a brute force solution such as running --forceExit via Jest when I run my tests. This feels like it is ignoring the root issue and might expose me to other kinds of mistakes down the line.

  2. My tests are spread across dozens of files, which means needing to invoke something in an afterAllTests would require a lot of redundancy and would introduce a code smell.

The Question

How can I ensure that sequelize connections are closed after tests finish, so they don't cause Jest to hang?

like image 678
slifty Avatar asked Feb 13 '20 22:02

slifty


1 Answers

Jest provides a way to create universal setup before every test suite. This means it is possible to leverage Jest's afterAll to close the sequelize connection pool without having to manually include it in every single test suite.

Example Jest config in package.json

  "jest": {
    ...
    "setupFilesAfterEnv": ["./src/test/suiteSetup.js"]
  }

Example suiteSetup.js

import models from '../server/models'
afterAll(() => models.sequelize.close())

// Note: in my case sequelize is exposed as an attribute of my models module.

Since the setupFilesAfterEnv is loaded before every test suite, this ensures that every Jest thread that opened a connection ultimately has that connection closed.

This doesn't violate DRY and it doesn't rely on a clunky --forceExit.

It does mean that connections will be closed that may never have been opened (which is a bit brute force) but that might be the best option.

like image 140
slifty Avatar answered Sep 18 '22 18:09

slifty