Vitest挂载Vue组件不渲染模板:Leaflet测试找不到DOM容器
Vue + Leaflet组件测试问题
组件代码
<script setup lang="ts"> import leaflet from "leaflet" let map onMounted(() => { console.log(document.querySelector("#map")) map = leaflet.map('map').setView([38.582958, -90.289045], 15); leaflet.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' }).addTo(map); leaflet.marker([38.582958, -90.289045]).addTo(map) }) </script> <template> <div id="map"></div> </template>
问题描述
该组件在浏览器中运行正常,但执行Vitest测试时,Leaflet提示找不到地图容器(DOM元素),添加地图功能前测试可正常运行。已查阅Vue测试工具文档,确认测试挂载组件应渲染DOM元素,现询问:
- 是否有正确挂载组件使DOM渲染的方法?
- 或应如何调整组件代码以支持测试时地图挂载?
更新内容
参考Mock Leaflet的建议,已Stub了Leaflet相关调用,测试已通过,但不确定这是否是前端Stub模块最简洁的方式。
解决方案
方案一:确保测试时DOM正确渲染
Vitest默认使用jsdom作为测试环境,组件挂载后DOM元素会被创建,但需要等待onMounted钩子执行完成再让Leaflet初始化。可以通过nextTick等待DOM更新和钩子执行:
import { mount } from '@vue/test-utils' import MapComponent from './MapComponent.vue' test('组件挂载后正确初始化地图', async () => { const wrapper = mount(MapComponent) await wrapper.vm.$nextTick() // 等待onMounted及DOM更新完成 // 可添加断言验证容器存在 expect(document.getElementById('map')).not.toBeNull() })
如果Leaflet仍无法找到容器,可尝试给地图容器设置固定宽高(jsdom中默认元素没有尺寸),比如在组件的style中添加:
<style scoped> #map { width: 100%; height: 400px; } </style>
方案二:Stub Leaflet模块(更简洁的测试方式)
单元测试的核心是验证组件逻辑,而非第三方库的实现。直接Mock Leaflet模块是更高效简洁的方式,避免依赖jsdom环境的DOM问题:
使用Vitest的vi.mock快速Mock
在测试文件顶部Mock整个Leaflet模块,模拟核心方法的行为:
import { mount } from '@vue/test-utils' import leaflet from 'leaflet' import MapComponent from './MapComponent.vue' // Mock Leaflet核心方法 vi.mock('leaflet', () => ({ map: vi.fn(() => ({ setView: vi.fn(), addTo: vi.fn() })), tileLayer: vi.fn(() => ({ addTo: vi.fn() })), marker: vi.fn(() => ({ addTo: vi.fn() })) })) test('组件正确调用Leaflet API初始化地图', async () => { const wrapper = mount(MapComponent) await wrapper.vm.$nextTick() // 断言map方法被正确调用 expect(leaflet.map).toHaveBeenCalledWith('map') // 断言setView参数正确 expect(leaflet.map().setView).toHaveBeenCalledWith([38.582958, -90.289045], 15) // 断言tileLayer和marker被初始化并添加到地图 expect(leaflet.tileLayer).toHaveBeenCalled() expect(leaflet.marker).toHaveBeenCalledWith([38.582958, -90.289045]) })
为什么这是最简洁的方式?
- 测试速度更快:无需加载真实Leaflet库和渲染地图瓦片
- 聚焦组件逻辑:只验证组件是否正确调用了Leaflet的API,无需关心第三方库的内部实现
- 避免环境差异:不受jsdom与真实浏览器环境的DOM差异影响
内容的提问来源于stack exchange,提问作者Chris Schmitz
相关产品推荐
相关产品推荐

