I was recently doing some work on a Node.JS project and I decided to use Knex.js with Objection.js for my data access to PostgreSQL.
Problem
In usual fashion, I wrote some tests to validate the behavior of the application and while writing the tests in Jest I found the tests were not exiting. I was getting the following 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 ran the tests with --detectOpenHandles
and the tests just hung. Nothing happened.
Solution
After some experimenting, I realized I was not destroying my Knex instance and that was keeping the tests from finishing.
In the afterAll()
method of Jest I’m now destroying the Knex instance so that the test can finish appropriately.
Here’s a very naive implementation that works for me as an example:
The model – Person.js:
'use strict'; const { Model } = require('objection'); const bcrypt = require('bcryptjs'); class Person extends Model { // Table name is the only required property static get tableName() { return 'persons'; } async $beforeInsert() { this.created_at = new Date().toISOString(); await this.updatePassword(); } async $beforeUpdate(opt, queryContext) { await super.$beforeUpdate(opt, queryContext); this.updated_at = new Date().toISOString(); await this.updatePasswordIfChanged(opt.old); } async updatePassword() { // not prod quality, do not copy, example only // bcrypt stretches should be much longer const hash = await bcrypt.hash(this.password, 1); this.password = hash; } async updatePasswordIfChanged(old) { if (old.password) { const passwordMatches = await bcrypt.compare(this.password, old.password); if (!passwordMatches) { await this.updatePassword(); } } } static get jsonSchema() { return { type: 'object', required: ['firstName', 'password'], properties: { firstName: { type: 'string', minLength: 2, maxLength: 255 } } } }; } module.exports = { Person };
The Test:
const Knex = require('knex'); const knexConfig = require('../knexfile'); const { Model } = require('objection'); const bcrypt = require('bcryptjs'); const { Person } = require('../models/Person'); const knex = Knex(knexConfig.test); Model.knex(knex); afterAll((done) => { knex.destroy(); done(); }); test('can update password when a user is already saved', async () => { const person = await Person.query().insert({ firstName: 'John', password: 'foo' }); const passwordMatches = await bcrypt.compare('foo', person.password) expect(person.password).not.toBe('foo'); expect(passwordMatches).toBeTruthy(); const updatedPerson = await person.$query().patchAndFetch({ password: 'bar' }); expect(person.password).not.toBe('bar'); expect(person.password).not.toBe('foo'); const updatedPasswordMatches = await bcrypt.compare('bar', person.password); expect(updatedPasswordMatches).toBeTruthy(); });
Now the test will pass and it will not get hung.
PS: Yes, this is a simple example and the code is not what I’d put into production, so please ignore the content there – this is just an example of how to fix the “Jest did not exit one second after the test run has completed.” issue.