Jest 检测到以下 1 个打开的句柄可能会阻止 Jest 退出:TCPSERVERWRAP

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

我在这里做一个基本的端到端测试,目前它失败了,但首先我无法摆脱打开的句柄。

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',
        })
      })
  })
})

知道什么阻碍了测试运行程序吗?

请注意,由于我使用的是 NestJs,所以我不需要在测试结束时使用 .end(done) 方法。

PS: 显然我在这个问题上有很多代码,我需要添加更多细节,但不知道我还能说些什么。

您再次重新创建整个应用程序 beforeEach,但仅在 afterAll 中将其拆除,这意味着您可能在此过程中泄漏了一些内存。您正在为 app 变量分配一个新实例,但很可能存在阻止垃圾收集器清除先前实例的隐藏引用 - 就像 request 函数获得的引用。

beforeEach 更改为 beforeAll,您应该可以开始了。

我还没有找到完美的解决方案,但目前我采用了这个解决方法:

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

forceExit 选项以某种方式杀死 openHandles 并解锁所有内容。 然而,我仍在寻找处理该问题的“正确方法”。

而不是 it 尝试使用 test 并将 done 作为参数传递并调用它。这对我有用。

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

// 每次测试

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

这就是问题所在

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

服务器未关闭,测试后保持打开状态。您需要创建一个变量来引用实例并在每次测试后关闭它。 我只是花了几个小时试图弄清楚这一点。希望这对遇到类似问题的人有所帮助。

这是您的代码示例以及我的修复想法:

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!')
  })

此外,我注意到您正在使用 beforeEachafterAll。您每次都在为每个测试创建一个新应用程序,所以我认为这也可能导致 HTTP 服务器出现一些问题。不过我不确定。

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', () => {
  ...
})

但是,这只是我的偏好,由您决定。 :)

更新:打算使用 beforeEach 而不是 beforeAll 因为我们需要在每次测试之前关闭服务器,而不是全局设置和拆卸。

更新 2:使用 async/await 否则,它将始终通过,因为请求是异步的并且不会完成,除非您等待它完成。

Toomuchrice4u 的回答对我很有帮助。我在组件使用的一项服务中有一个注销方法,所以我在 afterEach 中调用它,如下所示:

afterEach(async () => {

await userService.logout();

});