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

Viewed 24143

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.

7 Answers

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 still looking for the "proper way" of handling that issue.

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
    await return 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.

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.

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()
  })

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();

});

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"

// on each test

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

Related