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

Jest单元测试中mock navigator.language不生效问题

问题根因

两个核心问题导致用例失败:

  • 逻辑分支未命中:你的getLang函数优先判断navigator.languages,只要该值为真值就直接返回数组第一项,完全不会走到读取navigator.language的分支。Jest默认的jsdom测试环境自带navigator.languages属性,默认值为['en-US']。你在测试用例里直接打印navigator.language能看到mock值,是因为你手动读取了这个属性,但函数实际执行时走的是languages分支,直接返回默认的en-US,根本没访问navigator.language。
  • mock方式适配问题:jsdom环境下navigator的部分原生属性getter定义在原型链上,直接用jest.spyOn给当前navigator实例挂载getter,在部分场景下不会被模块内的代码正确读取到。
解决方案

方案1:补全分支mock(快速修复当前用例)

要测试navigator.language的返回逻辑,必须先把navigator.languages mock为假值,让代码走到目标分支,修改后的用例如下:

// utils.spec.ts
it('should return exact response language if there is one', () => {
  // 先屏蔽languages分支,让代码走到读取language的逻辑
  jest.spyOn(navigator, 'languages', 'get').mockReturnValueOnce(undefined);
  // 再mock目标language值
  jest.spyOn(navigator, 'language', 'get').mockReturnValueOnce('en-fr');
  expect(getLang()).toBe('en-fr');
});

修改后用例可直接通过。

方案2:全局属性描述符mock(通用稳定方案)

如果jest.spyOn在你的环境中仍有生效问题,可以直接通过Object.defineProperty修改全局navigator的属性定义,记得在用例执行后恢复原值,避免污染其他测试用例:

describe('Navigator', () => {
  // 缓存原始属性值,测试后恢复
  const originalLanguages = navigator.languages;
  const originalLanguage = navigator.language;

  afterEach(() => {
    // 恢复原生属性
    Object.defineProperty(navigator, 'languages', {
      get: () => originalLanguages,
      configurable: true
    });
    Object.defineProperty(navigator, 'language', {
      get: () => originalLanguage,
      configurable: true
    });
    jest.restoreAllMocks();
  });

  it('should return exact response language if there is one', () => {
    // 屏蔽languages分支
    Object.defineProperty(navigator, 'languages', {
      get: () => undefined,
      configurable: true
    });
    // 设置测试用的language值
    Object.defineProperty(navigator, 'language', {
      get: () => 'en-fr',
      configurable: true
    });
    expect(getLang()).toBe('en-fr');
  });

  it('should return 1st language if there array of languages', () => {
    Object.defineProperty(navigator, 'languages', {
      get: () => ['en-fr', 'en-US', 'en'],
      configurable: true
    });
    expect(getLang()).toBe('en-fr');
  });
});
避坑提示
  • mock属性前先梳理代码分支优先级,不要只mock目标属性就断言结果,先确认代码确实会走到读取该属性的逻辑。
  • 通过Object.defineProperty修改全局属性时必须加configurable: true,否则后续无法重新修改属性,会导致用例之间状态污染。
  • Jest 28及以上版本可使用官方提供的jest.replaceProperty方法替换对象属性,比jest.spyOn更适合全局属性类的mock场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 14:42:21