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

React中Mapbox单元测试报Failed to initialize WebGL错误

React集成Mapbox单元测试WebGL初始化失败解决方案

问题现象

React项目集成Mapbox GL JS运行单元测试时,即使将Map实例的testMode配置项设为true,仍抛出如下错误:

Error: Failed to initialize WebGL.
at new Map (C:\Users\...\node_modules\mapbox-gl\dist\mapbox-gl.js:35:427889)

关联代码

封装的Mapbox组件

const MapBox = (props: MapBoxProps) => {
  const { floorPlan, isTest } = props;
  const { width } = useWindowDimensions();
  const mapContainer = useRef(null);
  const map = useRef(null);
  const [lng, setLng] = useState(48);
  const [lat, setLat] = useState(25);
  const [zoom, setZoom] = useState(1.8);

  const createMapbox = () => {
    if (!isTest) {
      mapboxgl.accessToken = appConfig.MAPBOX_TOKEN;
    }
    const mb = new mapboxgl.Map({
      attributionControl: false,
      container: mapContainer.current,
      testMode: isTest,
      style: {
        version: 8,
        sources: {},
        layers: [
          {
            id: "background",
            type: "background",
            paint: {
              "background-color": "white",
            },
          },
        ],
      },
      center: [lng, lat],
      zoom: zoom,
      maxZoom: 6,
      dragRotate: false,
    });
    mb.addControl(new mapboxgl.NavigationControl({ showCompass: false }));
    return mb;
  };
  return <div
        data-cr="mapbox-container"
        ref={mapContainer}
      />;
}

单元测试代码

const renderMapBox = () => {
  return render(<CustomMapBox floorPlan={mockedFloorPlan} isTest={true} />);
};

describe("MapBox", () => {
  it("renders without error and two floors", () => {
    renderMapBox();
  });
})

已尝试排查方案

  • 更换相关依赖版本
  • 调整Jest配置项
  • 确认Mapbox testMode参数设为true

项目依赖版本

{
  "mapbox-gl": "^2.8.2",
  "react": "^17.0.2",
  "react-dom": "^17.0.2",
  "ts-jest": "^27.0.7",
  "jest": "^27.3.1"
}

根因说明

Mapbox的testMode配置仅会跳过访问令牌校验、禁用遥测上报、阻止真实外部资源请求,不会跳过WebGL上下文初始化流程。Jest默认使用的jsdom环境没有实现WebGL相关API,因此初始化Map实例时会触发WebGL初始化失败错误。
另外现有组件代码存在逻辑缺陷:定义了createMapbox初始化方法但未在DOM挂载完成后调用,若直接在渲染流程中执行初始化,还会出现ref指向DOM节点为空的问题。

可行解决方案

方案1:Mock WebGL上下文(需测试Map实例真实逻辑时使用)

  1. 在Jest配置中新增setup文件,比如jest.setup.ts,在setupFilesAfterEnv配置项中引入该文件
  2. 在setup文件中添加WebGL最小实现Mock,覆盖Mapbox初始化时调用的API:
    HTMLCanvasElement.prototype.getContext = jest.fn((type: string) => {
      if (['webgl', 'webgl2', 'experimental-webgl'].includes(type)) {
        return {
          createShader: jest.fn(),
          shaderSource: jest.fn(),
          compileShader: jest.fn(),
          getShaderParameter: jest.fn(() => true),
          createProgram: jest.fn(),
          attachShader: jest.fn(),
          linkProgram: jest.fn(),
          getProgramParameter: jest.fn(() => true),
          useProgram: jest.fn(),
          createBuffer: jest.fn(),
          bindBuffer: jest.fn(),
          bufferData: jest.fn(),
          viewport: jest.fn(),
          clearColor: jest.fn(),
          clear: jest.fn(),
          enable: jest.fn(),
          disable: jest.fn(),
          blendFunc: jest.fn(),
          getExtension: jest.fn(() => ({})),
          getParameter: jest.fn(() => 0),
          drawingBufferWidth: 1024,
          drawingBufferHeight: 1024,
        } as unknown as WebGLRenderingContext
      }
      return null
    })
    
  3. 修正组件初始化逻辑,将地图实例初始化放到useEffect中执行,确保DOM容器已完成挂载,同时增加卸载时的实例销毁逻辑:
    useEffect(() => {
      if (!mapContainer.current) return
      if (!isTest) {
        mapboxgl.accessToken = appConfig.MAPBOX_TOKEN
      }
      const mb = new mapboxgl.Map({
        attributionControl: false,
        container: mapContainer.current,
        testMode: isTest,
        style: {
          version: 8,
          sources: {},
          layers: [
            {
              id: "background",
              type: "background",
              paint: {
                "background-color": "white",
              },
            },
          ],
        },
        center: [lng, lat],
        zoom: zoom,
        maxZoom: 6,
        dragRotate: false,
      })
      mb.addControl(new mapboxgl.NavigationControl({ showCompass: false }))
      map.current = mb
    
      return () => {
        mb.remove()
        map.current = null
      }
    }, [isTest])
    

方案2:Mock整个mapbox-gl模块(单元测试推荐方案)

如果单元测试不需要验证Mapbox的内部交互逻辑,直接Mock整个mapbox-gl模块跳过真实初始化流程即可,执行效率更高:
在测试文件或者全局Jest setup文件中添加如下Mock:

jest.mock('mapbox-gl', () => ({
  Map: jest.fn(() => ({
    addControl: jest.fn(),
    remove: jest.fn(),
    on: jest.fn(),
    off: jest.fn(),
    flyTo: jest.fn(),
    // 按需补充代码中调用到的Map实例方法
  })),
  NavigationControl: jest.fn(() => ({})),
  // 按需补充代码中用到的其他mapbox-gl导出成员
}))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 23:24:23