Jest结合TypeScript如何测试window对象上定义的函数
问题原因
测试不通过的核心问题有3个:
- 模块加载时机问题:
Api.ts在顶层作用域直接执行const verifier = window.Verifier,这个赋值发生在模块被import的阶段,早于测试用例中mock window对象的时机。后续测试中修改window上的Verifier属性,不会影响模块内已经缓存的verifier常量,实际调用时取到的是模块加载时的undefined值,自然不会触发mock的request方法。 - Mock方式不合理:通过
jest.spyOn(window, "window", "get")直接替换整个window对象的返回值,会丢失window上原有的内置属性和jest依赖的全局API,容易触发额外报错。 - 异步断言时机错误:
fetchData是返回Promise的异步方法,测试中调用后没有等待方法执行完成就直接断言,会出现断言执行早于内部逻辑的问题。
修复步骤
1. 调整Api.ts中Verifier的读取时机
删除顶层缓存的verifier常量,改为在方法调用时动态读取window上的Verifier,保证能拿到测试阶段注入的mock值:
import "./global.d"; export class Api { constructor(private readonly id: string) {} public fetchData() { return new Promise<object>((resolve, reject) => { // 动态读取window.Verifier,不要在模块顶层缓存 window.Verifier.request({ request: { id: this.id }, onSuccess: (data: object) => { resolve(data); }, onFailure: () => { reject("Error!"); } }); }); } }
如果想减少重复书写window.Verifier,可以在类中增加一个getter做动态取值:
private get verifier() { return window.Verifier; }
后续方法中直接使用this.verifier.request()即可。
2. 调整测试用例的Mock逻辑和异步处理
不需要spy整个window对象,直接给window.Verifier赋值mock对象即可,同时等待异步方法执行完成后再做断言:
import { Api } from "./Api"; // 保存全局Verifier的原始值,测试结束后恢复 const originalVerifier = window.Verifier; describe("Test Apis", () => { afterEach(() => { // 恢复全局状态,清除mock window.Verifier = originalVerifier; jest.clearAllMocks(); }); it("should call the request method with correct params", async () => { const mockedRequest = jest.fn((payload) => { // 模拟成功回调,让Promise正常落定 payload.onSuccess({ res: "ok" }); }); // 直接挂载mock的Verifier到window上 window.Verifier = { request: mockedRequest } as any; const api = new Api("123"); // 等待异步方法执行完成 const res = await api.fetchData(); // 验证调用逻辑 expect(mockedRequest).toHaveBeenCalledTimes(1); expect(mockedRequest).toHaveBeenCalledWith(expect.objectContaining({ request: { id: "123" } })); expect(res).toEqual({ res: "ok" }); }); });
注意:确保你的jest配置中测试环境设置为
jsdom(jest默认配置就是jsdom),否则测试环境中不存在window对象。
内容的提问来源于stack exchange,提问作者user2532865
相关产品推荐
相关产品推荐

