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

如何在Jest+Supertest测试中正确关闭Polka服务器?

解决Polka中间件异常导致Jest无法退出及生产环境防护问题

问题根源

你的测试代码中,brokenMiddleware直接抛出未捕获的异常,Polka默认不会自动处理这类异常,导致请求连接挂起,Jest检测到未关闭的TCP句柄(TCPSERVERWRAP),最终无法正常退出。同时,这种未处理的异常在生产环境中会导致请求挂起甚至服务崩溃。

解决方案

1. 让Jest测试正常退出

核心是确保异常被捕获并正确终止请求响应,同时保证服务器关闭操作完成:

修改后的测试代码:

import polka, {Polka} from 'polka';
import { agent as request } from 'supertest';

function brokenMiddleware(req, res, next) {
  throw new Error("foobar");
}

describe('integration', () => {
  describe('middleware', () => {
    let testPolka: Polka;

    beforeEach(() => {
      testPolka = polka()
        .use(brokenMiddleware)
        .get('/', async (req, res) => {
          res.end('Hello world');
        })
        // 添加全局错误处理,确保响应正常结束
        .on('error', (err, req, res) => {
          if (!res.headersSent) {
            res.writeHead(500, { 'Content-Type': 'application/json' });
          }
          res.end(JSON.stringify({ error: err.message }));
        });
    });

    // 异步关闭服务器,确保句柄完全释放
    afterEach(async () => {
      if (testPolka?.server) {
        await new Promise<void>((resolve) => {
          testPolka.server.close(() => resolve());
        });
      }
    });

    it('should handle middleware errors gracefully', async () => {
      const response = await request(testPolka.handler)
        .get('/')
        .set('Accept', 'application/json');

      // 修正测试预期:中间件抛错应返回500状态
      expect(response.status).toEqual(500);
      expect(response.headers["Content-Type"]).toMatch(/json/);
    });
  });
});

关键改动:

  • 给Polka实例绑定error事件,捕获未处理的中间件异常,强制结束响应
  • 将afterEach改为异步,等待服务器关闭完成,确保TCP句柄释放
  • 修正测试预期,匹配中间件异常后的500响应状态

2. 生产环境防护:避免异常影响服务

生产环境中必须通过两层防护避免中间件异常引发的问题:

(1)中间件自身捕获异常

不管是同步还是异步中间件,都要自行捕获异常并传递给错误处理流程:

// 同步中间件
function safeMiddleware(req, res, next) {
  try {
    // 原业务逻辑(可能抛错的代码)
    throw new Error("foobar");
  } catch (err) {
    // 将错误传递给全局错误处理
    next(err);
  }
}

// 异步中间件
async function asyncSafeMiddleware(req, res, next) {
  try {
    // 异步操作
    await someAsyncOperation();
    next();
  } catch (err) {
    next(err);
  }
}

(2)全局错误处理

给Polka实例绑定全局error事件,统一处理所有未捕获的错误,确保请求能正常终止:

const app = polka()
  .use(safeMiddleware)
  .get('/', (req, res) => {
    res.end('Hello world');
  })
  .on('error', (err, req, res) => {
    console.error('Server error:', err);
    // 确保响应未发送时返回500
    if (!res.headersSent) {
      res.writeHead(500, { 'Content-Type': 'text/plain' });
      res.end('Internal Server Error');
    } else {
      // 已发送响应头时直接结束连接
      res.end();
    }
  });

app.listen(3000, () => console.log('Server running on port 3000'));

总结

  • 测试环境:通过全局错误处理确保请求正常结束,异步关闭服务器释放句柄,让Jest能正常退出
  • 生产环境:中间件自行捕获异常,配合全局错误处理,避免未捕获异常导致请求挂起或服务崩溃

内容的提问来源于stack exchange,提问作者Mikko Ohtamaa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 05:54:22