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

Node.js异步函数getSomeTask的Jest单元测试编写求助

修正后的Jest单元测试方案

一、先修正__mocks__/request.js

request库的get方法是基于回调的,mock函数需要支持触发传入的回调,所以修改为:

function get(options, callback) {
  // 留空让测试代码动态定义回调逻辑
  callback();
  return this;
}

module.exports = { get };

二、完整的Jest测试代码

const handler = require("../../handler");
const request = require("request");

// 告诉Jest使用mock版本的request模块
jest.mock("request");

describe("getSomeTask 函数测试", () => {
  // 每个测试前重置mock调用记录
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it("请求成功时,返回200状态和响应体", (done) => {
    // 模拟成功的响应数据
    const mockResponseBody = JSON.stringify({ Test: "test" });
    const mockResponse = { statusCode: 200 };

    // 模拟request.get触发成功回调
    request.get.mockImplementation((options, callback) => {
      callback(mockResponse, mockResponseBody);
      return {};
    });

    // 模拟res对象的方法,跟踪调用情况
    const res = {
      writeHead: jest.fn(),
      write: jest.fn(),
      end: jest.fn(),
      status: jest.fn().mockReturnThis(), // 支持链式调用,返回自身
      json: jest.fn()
    };

    // 调用目标函数
    handler.getSomeTask(res);

    // 等待异步回调执行完成后断言
    setTimeout(() => {
      // 验证request.get是否以正确的参数调用
      expect(request.get).toHaveBeenCalledWith(
        { url: "http://example.com" }, // 替换为实际的someTaskUrl值
        expect.any(Function)
      );

      // 验证响应方法的调用正确性
      expect(res.writeHead).toHaveBeenCalledWith(200, { 'content-type': 'application/json' });
      expect(res.write).toHaveBeenCalledWith(mockResponseBody);
      expect(res.end).toHaveBeenCalled();

      // 验证错误分支方法未被调用
      expect(res.status).not.toHaveBeenCalled();
      expect(res.json).not.toHaveBeenCalled();

      done();
    }, 0);
  });

  it("请求失败时,返回400错误", (done) => {
    // 模拟失败的响应
    const mockResponse = { statusCode: 500 };

    // 模拟request.get触发失败回调
    request.get.mockImplementation((options, callback) => {
      callback(mockResponse, null);
      return {};
    });

    // 模拟res对象
    const res = {
      writeHead: jest.fn(),
      write: jest.fn(),
      end: jest.fn(),
      status: jest.fn().mockReturnThis(),
      json: jest.fn()
    };

    handler.getSomeTask(res);

    setTimeout(() => {
      // 验证错误响应的调用正确性
      expect(res.status).toHaveBeenCalledWith(400);
      expect(res.json).toHaveBeenCalledWith({ error: 'Failed' });

      // 验证成功分支方法未被调用
      expect(res.writeHead).not.toHaveBeenCalled();
      expect(res.write).not.toHaveBeenCalled();
      expect(res.end).not.toHaveBeenCalled();

      done();
    }, 0);
  });
});

三、关键问题说明

  1. 原函数的异步处理问题
    虽然getSomeTask声明了async,但内部用的是request的回调模式,没有返回Promise,所以测试不能用await,必须用done参数让Jest等待异步回调执行完成。如果想改用await,可以用util.promisify把request.get包装成Promise形式。

  2. Mock request的正确姿势

    • 不需要直接引用__mocks__里的模块,jest.mock("request")会自动替换为mock版本。
    • 必须模拟回调触发:request.get是回调式API,mock时要主动调用传入的callback,传入预设的响应数据。
  3. Res对象的mock技巧

    • 给res的每个方法创建jest mock函数,用于跟踪调用记录和参数。
    • 对于Express风格的链式调用(如res.status().json()),需要让status方法返回res自身,用mockReturnThis()实现。
  4. 原测试代码的错误点

    • 原测试里的jest.fn().mockImplementation(...)没有赋值给request.get,完全无效。
    • 空的res对象没有模拟所需方法,调用时会直接报错。
    • 没有处理异步回调,测试会提前结束,导致断言失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 14:15:30