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

如何使用Jest正确mock FileReader解决测试中回调未覆盖的问题

问题本质

当前mock逻辑没有打通FileReader的事件触发链路:

  • 你定义的FileReaderMock中,onload 被初始化为空的jest mock函数,readAsText 也只是空的jest.fn(),没有任何实际行为
  • 业务代码执行时,虽然会把自定义的onload处理函数赋值到f.onload属性上,但整个测试流程从未触发这个回调,导致callback(e.target.result)这段逻辑从来没有被执行过,自然会被覆盖率标记为未覆盖。

修复方案

需要在mock层补完两个核心行为:

  1. 不要把onload初始化为空的jest.fn(),避免业务代码绑定回调时被无效的mock占位影响
  2. 补全readAsText的mock实现逻辑,让它可以模拟文件读取完成的流程,主动触发绑定好的onload回调

修正后的测试代码示例

const { testFunction } = require("./index");

class FileReaderMock {
  DONE = FileReader.DONE;
  EMPTY = FileReader.EMPTY;
  LOADING = FileReader.LOADING;
  readyState = 0;
  error = null;
  result = null;
  abort = jest.fn();
  addEventListener = jest.fn();
  dispatchEvent = jest.fn();
  onabort = jest.fn();
  onerror = jest.fn();
  // 不再初始化为jest.fn(),允许业务代码正常绑定自定义回调
  onload = null;
  onloadend = jest.fn();
  onloadprogress = jest.fn();
  onloadstart = jest.fn();
  onprogress = jest.fn();
  // 重写readAsText的实现,替代原有的空jest.fn()
  readAsText = jest.fn((blob) => {
    // 模拟文件读取完成的逻辑:设置返回值、构造事件、触发onload
    this.result = "mock读取得到的文本内容";
    const mockEvent = {
      target: {
        result: this.result
      }
    };
    this.onload(mockEvent);
  });
  readAsArrayBuffer = jest.fn();
  readAsBinaryString = jest.fn();
  readAsDataURL = jest.fn();
  removeEventListener = jest.fn();
}

const mockCallback = jest.fn();

describe("testFunction", () => {
  const fileReader = new FileReaderMock();

  // 每个测试前重置mock状态,避免用例间的污染
  beforeEach(() => {
    jest.clearAllMocks();
    fileReader.onload = null;
    fileReader.result = null;
  });

  jest.spyOn(window, "FileReader").mockImplementation(() => fileReader);

  it("should be called mockCallback and readAsText with the blob value", () => {
    const uintArray = new Uint8Array();
    testFunction(uintArray, mockCallback);

    expect(mockCallback).toHaveBeenCalledWith("mock读取得到的文本内容");
    expect(fileReader.readAsText).toHaveBeenCalledWith(new Blob([uintArray]));
  });
});

可选简化方案

如果不想在readAsText的mock里内置自动触发逻辑,也可以在测试用例里手动触发绑定好的onload回调,效果完全一致:

it("should be called mockCallback and readAsText with the blob value", () => {
  const uintArray = new Uint8Array();
  testFunction(uintArray, mockCallback);
  // 手动模拟onload事件触发
  fileReader.onload({ target: { result: "自定义测试返回值" } });

  expect(mockCallback).toHaveBeenCalledWith("自定义测试返回值");
  expect(fileReader.readAsText).toHaveBeenCalledWith(new Blob([uintArray]));
});

调整完成后,业务代码中onload内部的回调逻辑会被完整执行,覆盖率统计可以正常识别这部分代码。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.27 01:27:04