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

如何使用Jest为单测模拟自定义全局window变量?

问题描述

我正尝试为单个测试用例模拟自定义全局window变量。我有一个React组件MyComponent.tsx,它引用了服务器端index.html中定义的全局window变量:

<!-- index.html -->
<script>
 var globalVars = {
   myVar: "value"
 };
</script>
// MyComponent.tsx
function MyComponent() {
 const { myWindowVar } = constants;
   return <h1>{myWindowVar}</h1>;
}
// constants.ts
const constants = {
  myWindowVar: window?.globalVars?.myVar
};

我尝试了几种方法模拟该变量,但似乎都未在MyComponent渲染前生效,导致测试失败:

// MyComponent.test.js
test("Test mocked window variable", () => {
  // 1. 直接模拟global/window对象无效
  // global.globalVars = { myVar: "mocked value" };

  // 2. 此方法也无效
  // Object.defineProperty(window, "globalVars", {
  //   value: { myVar: "mocked value" },
  //   writable: true
  // });

  // 3. 监听window对象并更新变量同样无效
  const originalWindow = { ...window };
  const windowSpy = jest.spyOn(global, "window", "get");
  windowSpy.mockImplementation(() => ({
    ...originalWindow,
    globalVars: { myVar: "mocked value" }
  }));

  render(<MyComponent />);

  expect(screen.getByText("mocked value")).toBeInTheDocument();

  windowSpy.mockRestore();
});

请问是否可以实现?如何在组件渲染前,使用Jest为单个测试用例模拟这个自定义全局window变量?


解决方案

问题核心在于constants.ts中的constants对象是模块加载阶段就已计算完成的,而非组件渲染时动态读取。你之前的模拟操作虽然在组件渲染前执行,但constants在测试文件导入MyComponent的瞬间就已经初始化,导致模拟值无法被读取到。

以下是两种可行的解决思路:

思路1:将常量改为动态获取的函数

修改constants.ts,把静态对象改为返回值的函数,让组件每次渲染时都重新读取window变量:

// constants.ts
export const getConstants = () => ({
  myWindowVar: window?.globalVars?.myVar
});

组件中调用该函数:

// MyComponent.tsx
import { getConstants } from './constants';

function MyComponent() {
 const { myWindowVar } = getConstants();
   return <h1>{myWindowVar}</h1>;
}

此时再使用Object.defineProperty模拟window变量即可生效,因为每次渲染都会重新读取最新的window值。

思路2:测试中动态导入模块

如果不想修改业务代码,可以利用Jest的模块重置能力,确保模拟变量后再加载constants和组件:

// MyComponent.test.js
test("Test mocked window variable", async () => {
  // 重置所有已加载的模块
  jest.resetModules();
  
  // 模拟window上的全局变量
  Object.defineProperty(window, "globalVars", {
    value: { myVar: "mocked value" },
    writable: true
  });

  // 动态导入组件和测试库(此时constants会重新计算并读取模拟值)
  const { default: MyComponent } = await import('./MyComponent');
  const { screen, render } = await import('@testing-library/react');

  render(<MyComponent />);

  expect(screen.getByText("mocked value")).toBeInTheDocument();

  // 清理模拟
  delete window.globalVars;
});

这种方法的关键是让constants模块在模拟window之后才被加载,确保初始化时能读取到模拟值。

补充说明

  • 测试中直接操作window对象即可,无需修改global,因为浏览器环境中全局变量挂载在window上,Jest测试环境也遵循这一逻辑。
  • 若使用TypeScript,需添加全局类型声明避免报错:
// global.d.ts
declare global {
  interface Window {
    globalVars?: {
      myVar?: string;
    };
  }
}

export {};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 05:22:53