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

Node.js Azure Function使用Jest模拟POST请求报res.on is not a function问题

问题原因

你的Jest模拟逻辑存在两个核心错误:

  1. https.request的执行逻辑是:调用后先返回Request实例(需要支持write/end/on('error')方法),待响应就绪后才会执行传入的回调函数,将Response对象作为参数传入回调。你之前的写法混淆了返回值和回调参数的边界。
  2. 你在模拟中写了cb('res')({on: jest.fn()}),相当于先执行回调传入字符串'res',再把回调的返回值当做函数二次调用,最终传给业务代码的res实际是字符串'res',自然不存在on方法,抛出类型错误。

修复方案

重新梳理https.request的模拟逻辑,分别实现Request实例和Response实例的方法,并且根据不同测试场景触发对应的data/end/error事件即可。

完整测试代码示例

const https = require('https');
const fs = require('fs');
const myFunction = require('./index');

// 模拟依赖模块
jest.mock('https');
jest.mock('fs', () => ({
  readFileSync: jest.fn(() => '-----BEGIN CERTIFICATE-----\ntest_cert_content\n-----END CERTIFICATE-----\n')
}));

describe('Azure Function POST接口测试', () => {
  // 测试前预置环境变量
  beforeAll(() => {
    process.env.username = 'test_user';
    process.env.pwd = 'test_pwd';
    process.env.host = 'test.api.com';
    process.env.path = '/submit';
    process.env.port = '443';
  });

  // 每次测试前重置模拟状态
  beforeEach(() => {
    https.request.mockReset();
  });

  test('200响应正常返回场景', async () => {
    const mockReturnData = { code: 0, msg: 'success', result: {} };
    // 模拟Request实例
    const mockReq = {
      write: jest.fn(),
      end: jest.fn(),
      on: jest.fn()
    };
    // 实现https.request模拟逻辑
    https.request.mockImplementation((options, callback) => {
      // 模拟Response实例
      const mockRes = {
        statusCode: 200,
        statusMessage: 'OK',
        on: jest.fn((event, cb) => {
          if (event === 'data') {
            // 触发data事件传入响应数据
            cb(Buffer.from(JSON.stringify(mockReturnData)));
          }
          if (event === 'end') {
            // 触发end事件结束响应
            cb();
          }
        })
      };
      // 执行回调传入响应对象
      callback(mockRes);
      return mockReq;
    });

    const mockContext = { log: jest.fn() };
    const postData = JSON.stringify({ correlationId: 'test_001', content: 'test' });
    const result = await myFunction(mockContext, postData);

    // 断言执行结果符合预期
    expect(result).toEqual(mockReturnData);
    expect(mockReq.write).toHaveBeenCalledWith(postData);
    expect(mockReq.end).toHaveBeenCalledTimes(1);
  });

  test('401认证错误场景', async () => {
    const mockReq = {
      write: jest.fn(),
      end: jest.fn(),
      on: jest.fn()
    };
    https.request.mockImplementation((options, callback) => {
      const mockRes = {
        statusCode: 401,
        statusMessage: 'Unauthorized',
        on: jest.fn((event, cb) => {
          if (event === 'data') cb(Buffer.from(''));
          if (event === 'end') cb();
        })
      };
      callback(mockRes);
      return mockReq;
    });

    const mockContext = { log: jest.fn() };
    const postData = JSON.stringify({ correlationId: 'test_002' });
    await expect(myFunction(mockContext, postData)).rejects.toEqual({
      errorCode: 401,
      errorMessage: 'Unauthorized',
      errorDescription: 'Unauthorized'
    });
  });

  test('请求异常场景(域名无法解析)', async () => {
    const mockReq = {
      write: jest.fn(),
      end: jest.fn(),
      on: jest.fn((event, cb) => {
        if (event === 'error') cb(new Error('ENOTFOUND test.api.com'));
      })
    };
    https.request.mockImplementation(() => mockReq);

    const mockContext = { log: jest.fn() };
    const postData = JSON.stringify({ correlationId: 'test_003' });
    await expect(myFunction(mockContext, postData)).rejects.toEqual({
      errorCode: 404,
      errorMessage: 'ENOTFOUND test.api.com',
      errorDescription: 'ENOTFOUND test.api.com'
    });
  });
});

额外注意事项

如果需要测试非200/401的其他错误码、JSON解析异常等场景,只需要对应修改mockResponse的statusCode、返回的data内容即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 15:27:02