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

Vitest测试报错:window.URL.createObjectURL非函数,Mock无效求解答

问题分析与解决方案

核心原因排查

你遇到的问题本质是mock未在依赖模块访问window.URL前生效,或者mock的URL对象结构不符合库的预期,以下是具体原因和对应解决办法:


1. Mock时机晚于模块加载

azure-maps-control模块可能在你的mock代码执行前就已经被导入并读取了window.URL,导致后续的mock无法覆盖它的引用。

解决办法:
把mock代码放在测试文件的最顶部,在所有组件和依赖导入之前执行:

import { vi } from 'vitest';

// 先完成mock
vi.stubGlobal('URL', {
  createObjectURL: vi.fn(() => 'mock-blob-url'),
  revokeObjectURL: vi.fn(),
  constructor: function URL() {}, // 保留构造函数避免库报错
  toString: () => 'URL',
});

// 再导入组件和测试工具
import { renderWithGlobalContext } from './your-test-utils-path';
import ClientPortifolio from './ClientPortifolio';

describe('ClinetPortfolio component', () => {
  it('should render ClinetPortfolio', () => {
    const el = renderWithGlobalContext(<ClientPortifolio />, {
      providerProps: { value: { state: {} } },
      route: '/',
    });
    expect(true).toBeFalsy();
  });
});

2. 模块缓存导致mock不生效

如果之前的测试用例已经加载过azure-maps-control,Node.js的模块缓存会复用已加载的模块实例,此时新的mock不会对它生效。

解决办法:
在测试前重置模块缓存,并重新导入组件:

import { vi } from 'vitest';

describe('ClinetPortfolio component', () => {
  let ClientPortifolio;

  beforeAll(async () => {
    vi.resetModules(); // 清空模块缓存
    vi.stubGlobal('URL', {
      createObjectURL: vi.fn(),
      revokeObjectURL: vi.fn(),
      constructor: function URL() {},
    });
    // 重新导入组件
    ClientPortifolio = (await import('./ClientPortifolio')).default;
  });

  it('should render ClinetPortfolio', () => {
    const el = renderWithGlobalContext(<ClientPortifolio />, {
      providerProps: { value: { state: {} } },
      route: '/',
    });
    expect(true).toBeFalsy();
  });
});

3. URL对象mock不完整

部分库会依赖URL构造函数或其他原生属性,仅mockcreateObjectURL和revokeObjectURL可能导致库判定URL对象无效。

解决办法:
基于原生URL对象进行局部mock(如果测试环境存在原生URL):

beforeAll(() => {
  // 保留原生URL的其他属性,仅mock目标方法
  vi.spyOn(window.URL, 'createObjectURL').mockReturnValue('mock-url');
  vi.spyOn(window.URL, 'revokeObjectURL').mockImplementation(() => {});
});

如果测试环境中没有原生URL对象,先创建基础结构再mock:

beforeAll(() => {
  if (!window.URL) {
    window.URL = {};
  }
  window.URL.createObjectURL = vi.fn(() => 'mock-url');
  window.URL.revokeObjectURL = vi.fn();
  // 补充库可能用到的其他属性
  window.URL.toString = () => 'URL';
});

4. 测试上下文隔离导致mock失效

如果renderWithGlobalContext工具函数创建了独立的JSDOM上下文,全局的windowmock不会同步到这个隔离的上下文中。

解决办法:
修改工具函数,支持传入自定义window对象,或者在上下文创建后再执行mock:

it('should render ClinetPortfolio', () => {
  // 构造带mock的window对象
  const mockWindow = {
    ...globalThis.window,
    URL: {
      createObjectURL: vi.fn(),
      revokeObjectURL: vi.fn(),
    },
  };

  // 假设工具函数支持传入window参数
  const el = renderWithGlobalContext(<ClientPortifolio />, {
    providerProps: { value: { state: {} } },
    route: '/',
    window: mockWindow,
  });
  expect(true).toBeFalsy();
});

快速验证方法

在测试中添加打印语句,确认mock是否成功覆盖window.URL:

beforeAll(() => {
  vi.stubGlobal('URL', {
    createObjectURL: vi.fn(),
    revokeObjectURL: vi.fn(),
  });
  console.log('Current window.URL:', window.URL); // 检查输出是否为mock对象
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 10:42:48