Jest has detected the following 1 open handle potentially keeping Jest from exiting: TCPSERVERWRAP
Asked Answered
B

11

39

I am doing a basic end to end testing here, for the moment it's failing, but first I can't get rid of the open handle.

Ran all test suites.

Jest has detected the following 1 open handle potentially keeping Jest from exiting:

  ●  TCPSERVERWRAP

      40 |     }
      41 |     return request(app.getHttpServer())
    > 42 |       .post('/graphql')
         |        ^
      43 |       .send(mutation)
      44 |       .expect(HttpStatus.OK)
      45 |       .expect((response) => {

      at Test.Object.<anonymous>.Test.serverAddress (../node_modules/supertest/lib/test.js:61:33)
      at new Test (../node_modules/supertest/lib/test.js:38:12)
      at Object.obj.<computed> [as post] (../node_modules/supertest/index.js:27:14)
      at Object.<anonymous> (app.e2e-spec.ts:42:8)
import { Test, TestingModule } from '@nestjs/testing'
import { HttpStatus, INestApplication } from "@nestjs/common";
import * as request from 'supertest'
import { AppModule } from '../src/app.module'

describe('AppController (e2e)', () => {
  let app: INestApplication

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile()

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

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

  it('/ (GET)', () => {
    return request(app.getHttpServer())
      .get('/')
      .expect(HttpStatus.OK)
      .expect('Hello World!')
  })

  it('mutation', async () => {
    const mutation = {
      query: `mutation Create($title: String!) {
        create(title: $title) {
          id,
          title
        }
      }`,
      variables: {
        title: 'Mon programme',
      },
    }
    return request(app.getHttpServer())
      .post('/graphql')
      .send(mutation)
      .expect(HttpStatus.OK)
      .expect( (response) => {
        expect(response.body).toBe({
          id: expect.any(String),
          title: 'Mon programme',
        })
      })
  })
})

Any idea what's blocking the test runner ?

Note that, as I am using NestJs, I shouldn't need to use the .end(done) method at the end of the test.

PS: apparently I have to much code on this question and I need to add some more details, but have no clue what I can say more.

Bartholomeus answered 19/7, 2021 at 8:57 Comment(0)
B
42

I still haven't found a perfect solution, but for the moment I went for this workaround :

jest --config ./test/jest-e2e.json --forceExit

The --forceExit option kill the openHandles somehow and unlock everything. Yet, I'm still looking for the "proper way" of handling that issue.

Bartholomeus answered 21/7, 2021 at 12:59 Comment(5)
thank you so much I have spent hours trying to mock the setInterval() function causing problems but it didn't work. This is an easy, simple fix.Brescia
I had --forceExit and --detectOpenHandles in my script after removed --detectOpenHandles it solved "test:e2e": "jest --config ./test/jest-e2e.json --forceExit"Malign
adding --no-cache --watchAll fixed for me, as answered by tonskton. my script: jest --config ./test/jest-e2e.json --detectOpenHandles --watchAll --no-cacheSynchrotron
i don't think you need --detectOpenHandles in your script since that's more for debugging. I guess if you want to always have that output, then sure, but it's not necessary to get jest to exit.Sennit
--watchAll fixes the issue for me too but then requires interaction. This isn't great in a CI/CD build though.Exclude
A
23

This is the problem right here

  it('/ (GET)', () => {
    return request(app.getHttpServer())
                  ^^^^^^^^^^^^^^^^^^^^^
      .get('/')
      .expect(HttpStatus.OK)
      .expect('Hello World!')
  })

The server isn't being closed and remains open after the test. You need to create a variable to reference the instance and close it after each test. I just spent a couple of hours trying to figure this out. And hope this helps anyone experiencing similar issues.

Here is an example of your code with my idea for a fix:

describe('AppController (e2e)', () => {
  let app: INestApplication
  let server: SERVER_TYPE

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile()

    app = moduleFixture.createNestApplication()
    await app.init()
    // Reference the server instance
    server = app.getHttpServer()
  })

  afterEach(async () => {
    await app.close()
    // Close the server instance after each test
    server.close()
  })

  it('/ (GET)', async () => {
    // Make the request on the server instance
    return await request(server)
      .get('/')
      .expect(HttpStatus.OK)
      .expect('Hello World!')
  })

Also, I noticed you're using beforeEach and afterAll. You're creating a new app each time for each test so I think that could also cause some issues for the HTTP server. I'm not certain on that though.

import { Test, TestingModule } from '@nestjs/testing'
import { HttpStatus, INestApplication } from "@nestjs/common";
import * as request from 'supertest'
import { AppModule } from '../src/app.module'

beforeEach(() => {
  ...
})

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

describe('tests', () => {
  ...
})

But, that's just my preference, up to you. :)

UPDATE: Meant to use beforeEach not beforeAll because we need to close the server before EACH test, not a global setup and teardown.

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.

Amandy answered 17/4, 2022 at 4:14 Comment(0)
N
9

You are re-creating the whole app again beforeEach, but tearing it down only in afterAll, which means you are probably leaking some memory along the way. You are assigning a new instance to the app variable, but there are most likely hidden references that prevent the garbage colletor from clearing the previous instance - like the reference that the request function got.

Change beforeEach to beforeAll and you should be good to go.

Nimesh answered 19/7, 2021 at 9:17 Comment(4)
Thank for the answer. But unfortunately it has no effect.Bartholomeus
@AMehmeto hm, strange, are you running multiple tests in parallel or just one file?Nimesh
Just one file only.Bartholomeus
@AMehmeto my second guess is that something is happening within your app that keeps it from exiting. Have you read through this thread, expecially the linked comment?Nimesh
H
7

To anyone who still encountering error even though you already close the connection and its and intermittent error you can try to add --no-cache --watchAll. Below is the full syntax:

"test": "jest --watchAll --no-cache --detectOpenHandles"

Hanoverian answered 28/9, 2022 at 14:0 Comment(1)
I don't know why, but this is the only thing that worked for me, --no-cache --watchAll did the trick. --watchAll is not optional which makes no sense to me, however I'm happy it works, I am using mongoose and mongodb-memory-serverSynchrotron
H
1

Had the same issue.

    "test:e2e": "jest --config ./test/jest-e2e.json --no-cache --detectOpenHandles",

worked fine for me

Hufuf answered 22/1, 2023 at 22:59 Comment(1)
but it seems like not the best option due to: "The cache should only be disabled if you are experiencing caching related problems. On average, disabling the cache makes Jest at least two times slower." from official docsHufuf
O
1

For me I had to close connection with the server and with my db's client.

afterAll(async () => {
 await server.close();
 await pool.end();
});
Obeah answered 13/12, 2023 at 10:49 Comment(0)
W
0

Instead of it try using test and pass done as a parameter and call that. This worked for me.

test('mutation', async (done) => {
    const mutation = {
      query: `mutation Create($title: String!) {
        create(title: $title) {
          id,
          title
        }
      }`,
      variables: {
        title: 'Mon programme',
      },
    }
    const response = request(app.getHttpServer())
      .post('/graphql')
      .send(mutation)
     expect(response).to.be(HttpStatus.Ok)
     done()
  })
Williford answered 21/9, 2021 at 16:50 Comment(3)
No joy here. So far only the --forceExit options works for me.Braxton
it and test are the sameAssailant
Actually there are two approaches mixed here, either use async or the done callback. You will notice using both isn't possible in Typescript, where it refuses to run this code.Sexagesimal
H
0

// on each test

it('the description', (done) => {
        request(app)
          .get('/some-path')
          .end(done);
  });

Homerus answered 2/1, 2022 at 7:28 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Mariammarian
R
0

Answer from Toomuchrice4u has helped me. I had a logout method in one of the services that component uses, so I called it in afterEach, like this:

afterEach(async () => {

await userService.logout();

});

Robomb answered 19/5, 2022 at 13:0 Comment(0)
C
0

Had a similar problem with mongoose, I just added process.exit() in my global teardown file and that solved everything.

Calida answered 13/6 at 11:3 Comment(0)
P
-1

Also check your package.json file in the scripts section to find the test:e2e key. Check the value of it and remove the parameter --detectOpenHandles. the key-value pair of script can be as follow: "test:e2e": "jest --config ./test/jest-e2e.json --forceExit"

Phi answered 12/6, 2022 at 14:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.