You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何将listen()移入test.before钩子后测试套件会永久阻塞?

Node.js测试套件移入test.before后永久阻塞的原因

现象说明

下面这段测试代码可以正常运行:

import assert from 'node:assert/strict';
import http from 'node:http';
import test from 'node:test';

test.describe(() => {
  const server = http.createServer((request, response) => {
    response.statusCode = 200;
    response.setHeader('Content-Type', 'text/plain');
    response.setHeader('Content-Length', '12');
    response.end('hello, world');
  });
  server.listen(8000, 'localhost');

  test.after(() => {
    server.closeIdleConnections();
    server.close();
  });

  test.test(async () => {
    const response = await fetch('http://localhost:8000/');
    assert.equal(response.status, 200);
    assert.equal(response.headers.get('Content-Type'), 'text/plain');
    assert.equal(response.headers.get('Content-Length'), '12');
    assert.equal(await response.text(), 'hello, world');
  });
});

但如果将server.listen()调用移入test.before钩子,测试套件就会永久阻塞:

import assert from 'node:assert/strict';
import http from 'node:http';
import test from 'node:test';

test.describe(() => {
  const server = http.createServer((request, response) => {
    response.statusCode = 200;
    response.setHeader('Content-Type', 'text/plain');
    response.setHeader('Content-Length', '12');
    response.end('hello, world');
  });

  test.before(() => {
    server.listen(8000, 'localhost');
  });

  test.after(() => {
    server.closeIdleConnections();
    server.close();
  });

  test.test(async () => {
    const response = await fetch('http://localhost:8000/');
    assert.equal(response.status, 200);
    assert.equal(response.headers.get('Content-Type'), 'text/plain');
    assert.equal(response.headers.get('Content-Length'), '12');
    assert.equal(await response.text(), 'hello, world');
  });
});

原因分析

核心问题在于test.before的同步特性:它不会自动等待内部的异步操作完成。

server.listen()是异步调用,它会立即返回,实际完成端口监听的逻辑是在后续的事件循环中执行的。在第一个正常运行的代码里,listen()直接写在describe块中,虽然也是异步,但后续的测试用例是异步函数,会进入事件循环等待,等服务器完成监听后再执行fetch请求。

但移入test.before后,因为钩子是同步函数,Node.js测试框架会直接跳过等待,立刻执行后续的测试用例——这时候服务器大概率还没完成监听,fetch请求会因为连接不上而卡住,最终导致整个测试套件永久阻塞。

要解决这个问题,只需要把test.before改成异步钩子,明确等待server.listen()完成:

// 方式1:使用回调函数
test.before((done) => {
  server.listen(8000, 'localhost', done);
});

// 方式2:用Promise包装成async钩子
test.before(async () => {
  await new Promise(resolve => server.listen(8000, 'localhost', resolve));
});

这样框架会等待服务器完成监听后再执行测试用例,就不会出现阻塞问题了。


内容的提问来源于stack exchange,提问作者Géry Ogam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 19:07:54