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

如何用Jest测试与Mock Node.js readline?测试报错求助

问题分析

你的测试失败有两个核心原因:

  1. 错误抛出时机不匹配:throw语句是在rl.question的回调函数里执行的,属于异步代码范畴,但expect(() => app.play()).toThrow只能捕获同步执行的函数抛出的错误,回调里的异步错误无法被同步捕获逻辑抓到。
  2. Mock未真正生效:你在测试中创建的rl实例和App模块内部初始化的rl不是同一个对象,所以你给测试里的rl赋值mockQuestion,根本不会影响到App里实际使用的rl实例。
修复方案

步骤1:正确Mock readline模块

我们需要让App使用我们Mock出来的readline实例,而非它自身创建的实例。可以用Jest的jest.mock直接mock整个readline模块:

步骤2:处理异步错误的捕获

因为错误是在回调里异步抛出的,必须用Jest的异步测试方式来捕获,比如使用done回调,或者把回调逻辑包装成Promise。

方案一:不修改原App代码的测试写法

const readline = require("readline");
const App = require("../src/App");

// Mock readline模块的createInterface方法,让它返回我们可控的mock实例
jest.mock("readline", () => {
  const mockRl = {
    question: jest.fn()
  };
  return {
    createInterface: jest.fn(() => mockRl)
  };
});

test("输入4个数字时抛出错误", (done) => {
  // 获取mock出来的rl实例
  const mockRl = readline.createInterface();
  
  // 重写question的mock逻辑,触发回调并监听未捕获错误
  mockRl.question.mockImplementation((question, callback) => {
    // 监听全局未捕获异常,捕获回调里抛出的错误
    process.once('uncaughtException', (error) => {
      expect(error.message).toBe("[ERROR] You have to input 3 numbers");
      done(); // 告诉Jest测试已完成
    });
    callback("1234");
  });

  const app = new App();
  app.play();
});

方案二:修改App代码为Promise风格(更简洁易测)

如果允许调整原业务代码,把play方法改成返回Promise的形式,测试会更直观:

修改后的index.js

const readline = require("readline");
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

class App {
  play() {
    return new Promise((resolve, reject) => {
      rl.question("Input Numbers ", (userInput) => {
        const userNumbers = userInput.split("").map(Number);
        if (userNumbers.length !== 3) {
          reject(new Error("[ERROR] You have to input 3 numbers"));
          return;
        }
        resolve(userNumbers);
      });
    });
  }
}

module.exports = App;

对应的测试代码

const readline = require("readline");
const App = require("../src/App");

jest.mock("readline", () => {
  const mockRl = {
    question: jest.fn()
  };
  return {
    createInterface: jest.fn(() => mockRl)
  };
});

test("输入4个数字时抛出错误", async () => {
  const mockRl = readline.createInterface();
  
  mockRl.question.mockImplementation((question, callback) => {
    callback("1234");
  });

  const app = new App();
  // 用async/await配合rejects断言捕获异步错误
  await expect(app.play()).rejects.toThrow("[ERROR] You have to input 3 numbers");
});
关键说明
  • Mock模块而非单独实例:用jest.mock替换整个readline模块的createInterface方法,确保App全程使用我们的mock实例,避免出现实例不匹配的问题。
  • 异步错误处理:要么通过监听全局uncaughtException捕获回调里的错误,要么把异步逻辑包装成Promise,用async/await配合expect().rejects来完成测试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 09:15:38