如何测试Vue mounted/unmounted中的addEventListener与removeEventListener
问题根因
你现在的代码存在两个核心问题,也是覆盖率缺失、逻辑不生效的原因:
onMounted里存在笔误:写了window.addEventListener.addEventListener,正确调用应该是window.addEventListener,这行本身就会抛出运行时错误- 事件绑定/移除的写法本身就不符合DOM API规范:
addEventListener和removeEventListener必须传入完全相同的函数引用才能匹配,你两次传入的都是临时定义的匿名箭头函数,是两个完全独立的函数实例,哪怕走到onUnmounted逻辑,也根本移除不了之前绑定的监听,会造成内存泄漏 - 覆盖率统计不到
removeEventListener所在行,本质是你的测试用例从来没有触发过组件卸载动作,onUnmounted生命周期钩子根本没执行,对应代码自然不会被标记为已覆盖
第一步:修正组件业务代码
先把事件回调抽离为固定引用的函数,修正笔误:
import { ref, onMounted, onUnmounted } from 'vue' const isMobile = ref(false) // 抽离为独立函数,保证绑定、移除时拿到的是同一个引用 const handleWindowResize = () => { isMobile.value = setIsMobile() } onMounted(() => { // 修正多写的一层.addEventListener笔误 window.addEventListener('resize', handleWindowResize) }) onUnmounted(() => { // 传入和绑定时完全一致的函数引用 window.removeEventListener('resize', handleWindowResize) }) isMobile.value = setIsMobile()
第二步:编写对应测试用例
以Vue Test Utils + Vitest/Jest 测试栈为例,测试需要同时覆盖挂载绑定、卸载移除两个场景,核心是手动触发组件卸载动作,让onUnmounted钩子执行:
import { mount } from '@vue/test-utils' import TargetComponent from './TargetComponent.vue' import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' describe('window resize 事件监听逻辑', () => { let addEventSpy let removeEventSpy beforeEach(() => { // 监听window的事件方法,校验调用参数 addEventSpy = vi.spyOn(window, 'addEventListener') removeEventSpy = vi.spyOn(window, 'removeEventListener') }) afterEach(() => { // 还原spy,避免用例之间互相影响 addEventSpy.mockRestore() removeEventSpy.mockRestore() }) it('组件挂载时正确绑定resize事件监听', () => { mount(TargetComponent) expect(addEventSpy).toHaveBeenCalledWith('resize', expect.any(Function)) }) it('组件卸载时正确移除resize事件监听', () => { const wrapper = mount(TargetComponent) // 核心操作:手动触发组件卸载,执行onUnmounted内的逻辑 wrapper.unmount() // 断言移除方法被调用 expect(removeEventSpy).toHaveBeenCalledWith('resize', expect.any(Function)) // 更严谨的校验:确认绑定和移除使用的是同一个函数引用 const [, bindCallback] = addEventSpy.mock.calls.find(item => item[0] === 'resize') const [, removeCallback] = removeEventSpy.mock.calls.find(item => item[0] === 'resize') expect(bindCallback).toBe(removeCallback) }) })
额外提示:如果测试环境是jsdom,触发resize事件后也可以断言
isMobile的值是否符合预期,进一步验证回调逻辑的正确性。
内容的提问来源于stack exchange,提问作者artuyees
相关产品推荐
相关产品推荐

