H. Majury
H. Majury

Reputation: 376

Jest detects open handle with Express app

I've been trying to figure this out for a while now, but with no luck. I initially had the issue when performing simple integration tests with a MongoDB database, but I've stripped the code right down and made it as simple as I can. The only thing I have running is a single test file:

// blah.test.js
const express = require('express');
const app = express();

describe('test block', () => {
    let server = null;

    beforeEach(() => {
        server = app.listen(3000, () => console.log('Listening on port 3000'));
    });

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

    it('should pass the test', () => {
        expect(1).toBe(1);
    });
});

The test passes, but Jest informs me of an open handle which could prevent it from exiting:

enter image description here

On this occasion, it did exit, but still notices an open handle. I would like to know how to safely close this, without forcing exit. With previous tests, I would get the same open handle warning, but also with an error stating that Jest didn't exit one second after completing the tests.

I also tried using beforeAll() and afterAll(), instead of beforeEach() and afterEach(), but still get the same problem. I know the test doesn't require the server on this occasion, but I was trying to make it as simple as possible to try to find the issue with opening and closing the server.

UPDATE: I noticed that it seems to work fine as soon as I add a second test file:

// blah2.test.js
const express = require('express');
const app = express();

describe('test block', () => {
    let server = null;

    beforeEach(() => {
        server = app.listen(3000, () => console.log('Listening on port 3000'));
    });

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

    it('should pass the test', () => {
        expect(2).toBe(2);
    });
});

No open handles with two tests running

If anyone can share any ideas as to why this happens, it would be much appreciated.

I am using:

Express: 4.16.3

Jest: 23.5.0

NPM: 6.3.0

Upvotes: 20

Views: 24430

Answers (3)

Bruno
Bruno

Reputation: 6449

So I solved the issue by abstracting all the app logic to an app.js file:

const express = require('express');
const routes = require('./routes');
const app = express();

app.use('/api', routes);

module.exports = app

Then creating a server.js file with all my server logic:

const app = require('./app.js')
const PORT = process.env.PORT || 3000
app.listen(PORT, () => console.log(`Listening on port: ${PORT}`))

Then just import app.js into your test file like this:

const app = require('../app.js')

Works like a charm!

Upvotes: 24

jjgg
jjgg

Reputation: 670

I had the same issue and I was able to fix it yesterday. The issue is calling app.listen(). You should avoid calling app.listen in your app.js file or jest test file. Just export the app with routes from app.js. Then, you can use supertest to make the request in your test.

// in your app.js
const app = express();
// add routes and middleware
module.exports = app;

// in your test file
import app from './app.js';
import supertest from 'supertest';

describe('test block', () => {
  it('test route', async (done) => {
    const res = await supertest(app).get('/').expect(200);
    done();
  });
});

Upvotes: 5

Chris
Chris

Reputation: 1230

Try this:

beforeAll(async () => {
    // let's create a server to receive integration calls
    app = express();
    app.get('/', (req, res) => res.send('Hello World!'));

    await new Promise((resolve) => {
        app.listen(6666, () => {
            console.log('Example app listening on port 6666!');
            return resolve();
        });
    });
});

afterAll(() => { console.log('closing...'); app.close(); });

Upvotes: 1

Related Questions