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

Node中用Jest模拟NDJSON流式端点时遇超时问题求助

问题分析

测试超时的核心原因是:你用ndjson.stringify(EventsResponseBody)生成的Transform流,在测试环境中没有被正确写入数据并结束,导致getEvents里监听的end事件永远不触发,Promise一直处于pending状态,最终触发Jest的超时。

解决方案

1. 提前生成NDJSON格式字符串,构造可自动结束的可读流

不要直接用ndjson.stringify的流作为Response的body,而是先把数据转成NDJSON字符串,再用Node.js的Readable.from创建可读流,这样流会自动结束,触发end事件。

2. 修改测试代码

import * as fetch from 'node-fetch';
const { Readable } = require('stream');
import getEvents from './getEvents';
import { EventResponseBody } from '../mocks/mockData';

describe('Events resolver', () => {
  let fetchMock: jest.SpyInstance;
  
  beforeAll(() => {
    fetchMock = jest.spyOn(fetch, 'default').mockImplementation(() => undefined);
  });

  afterEach(() => {
    fetchMock.mockReset();
  });

  afterAll(() => {
    fetchMock.mockRestore();
  });

  it('returns events for an id', async () => {
    // 把数组转成NDJSON格式字符串,每个JSON元素单独一行
    const ndjsonStr = EventResponseBody.map(JSON.stringify).join('\n') + '\n';
    // 基于NDJSON字符串创建可读流
    const responseBody = Readable.from(Buffer.from(ndjsonStr));
    const response = new fetch.Response(responseBody, { status: 200 });
    
    fetchMock.mockReturnValue(Promise.resolve(response));

    const result = await getEvents(url, token, { id: '1' });
    expect(fetchMock).toHaveBeenCalledTimes(1);
    // 可选:验证返回结果是否与原数据一致
    expect(result).toEqual(EventResponseBody);
  });
});

3. 优化getEvents代码(可选,修复Promise反模式)

你的getEvents使用了async修饰的Promise构造器,属于反模式,同时补充流错误监听避免遗漏异常:

const getEvents = <T>(url: string, token: string, params: { id: string }) => {
  return new Promise<T[]>((resolve, reject) => {
    fetch(`${url}${params.id}`, {
      headers: {
        Accept: 'application/x-ndjson',
        Authorization: `Bearer ${token}`,
      },
    })
      .then(response => {
        const events: T[] = [];
        response.body
          .pipe(ndjson.parse())
          .on('data', (data: T) => events.push(data))
          .on('end', () => resolve(events))
          .on('error', reject); // 监听流错误,避免未捕获异常导致Promise挂起
      })
      .catch(reject);
  });
};
关键说明
  • ndjson.stringify是Transform流,需要手动写入数据并调用end()才会结束流。测试中直接用它作为Response body,流会一直处于等待写入的状态,永远不会触发end事件。
  • 用Readable.from创建的流,当所有数据被读取完毕后会自动触发end事件,这样getEvents中的Promise就能正常resolve,测试不会超时。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 11:52:21