react-leaflet配合react-testing-library测试报useLeafletContext上下文缺失错误如何解决
问题根因
Leaflet 依赖浏览器专属的 DOM API 和全局对象,Jest 默认的 jsdom 运行环境不支持这些能力,导致 <MapContainer> 组件在测试环境初始化失败,没有正常生成 Leaflet 上下文,所以所有调用 useLeafletContext() 的子组件都会抛出找不到上下文的错误。你之前在外层手动加 LeafletProvider 无效,是因为空的上下文值无法满足子组件的调用需求,且内部 <MapContainer> 本身初始化失败的问题没有解决。
推荐解决方案:Mock 第三方地图组件
单元测试不需要真实渲染地图组件,直接 mock 掉 react-leaflet 和 react-leaflet-draw 相关的组件和 hook 即可,操作步骤如下:
步骤1:配置 Jest 全局 mock
在你的 Jest 初始化文件(通常为 jest.setup.ts/jest.setup.js,如果没有就新建)中添加以下 mock 代码:
import React from 'react'; // Mock react-leaflet 所有相关组件和hook jest.mock('react-leaflet', () => ({ ...jest.requireActual('react-leaflet'), // 把MapContainer mock成普通容器,直接返回子元素 MapContainer: ({ children }: { children: React.ReactNode }) => ( <div data-testid="mock-map-container">{children}</div> ), TileLayer: () => <div data-testid="mock-tile-layer" />, FeatureGroup: ({ children }: { children: React.ReactNode }) => ( <div data-testid="mock-feature-group">{children}</div> ), // Mock useLeafletContext返回你业务代码用到的方法 useLeafletContext: () => ({ map: { contains: jest.fn(() => true), distanceTo: jest.fn(() => 50), // 其他业务用到的map方法都可以在这里加mock返回值 } }) })); // Mock react-leaflet-draw 组件 jest.mock('react-leaflet-draw', () => ({ EditControl: () => <div data-testid="mock-edit-control" /> })); // Mock leaflet的CSS文件,避免Jest解析CSS报错 jest.mock('leaflet/dist/leaflet.css', () => ({}));
步骤2:关联 Jest 初始化文件
在你的 jest.config.js 或者 package.json 的 jest 配置段,添加以下配置指向初始化文件:
{ "jest": { "setupFilesAfterEnv": ["<rootDir>/src/jest.setup.ts"] } }
步骤3:回滚自定义渲染函数
把你之前修改的 renderWithProviders 回滚到原来的版本,不需要额外加 LeafletProvider 或者 MapContainer 包裹,原有测试代码即可正常运行。
可选方案:非Mock的集成测试方案
如果你需要真实渲染地图做集成测试,可以安装 jest-environment-jsdom-global 环境,在测试初始化时手动挂载 Leaflet 全局对象 window.L,并提前创建好 mock 的 map 实例注入上下文,但这个方案配置复杂度较高,单元测试场景优先推荐使用Mock方案。
内容的提问来源于stack exchange,提问作者krallj

