如何使用Vitest测试Vue组件中的defineModel?
Toast组件测试断言失败:expected true to be false 解决方法
问题场景
使用Vue 3的defineModel实现Toast组件的显示控制,添加自动隐藏的定时器逻辑后,编写Vitest测试时断言modelValue为false失败,报错AssertionError: expected true to be false。
组件代码(Toast.vue)
<template> <div v-if="show"> {{ text }} </div> </template> <script setup lang="ts"> interface ToastType { text: string time?: number } const props = defineProps<ToastType>() const show = defineModel({default: false }) watch(show, () => { if (show.value) { setTimeout(() => { show.value = false }, props.time || 2000) } }) </script>
测试代码
import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest' import { shallowMount } from '@vue/test-utils' import Toast from '../Toast.vue' const props = { text: 'hello toast', time: 2500, modelValue: false } beforeEach(() => { vi.useFakeTimers() }) afterEach(() => { vi.useRealTimers() }) describe('Toast.vue', () => { it('Toast Props', async () => { const wrapper = shallowMount(Toast, { propsData: props }) await wrapper.setProps({ modelValue: true }) vi.runAllTimers() await wrapper.vm.$nextTick() expect(wrapper.emitted()).toHaveProperty('update:modelValue') expect(wrapper.emitted('update:modelValue')).toHaveLength(1) // 错误:AssertionError: expected true to be false expect(wrapper.vm.modelValue).toBe(false) }) })
问题原因
- Vue单向数据流约束:
modelValue是父组件传入的props,Vue中props是只读的,子组件通过defineModel修改值时,本质是向父组件发送update:modelValue事件,不会自动修改子组件自身的modelValueprops值。测试中wrapper.vm.modelValue获取的是父组件最初传入(或后续通过setProps设置)的props值,父组件未更新时,该值保持true。 - defineModel的工作机制:
defineModel返回的是一个双向绑定的ref对象(即代码中的show),它内部会同步props和emit事件,但组件实例上的modelValue仍然是原始的props,并非这个ref的当前值。
解决方案
方案1:断言update:modelValue事件的参数
组件触发的update:modelValue事件携带了正确的目标值,直接断言该事件的参数即可验证逻辑正确性:
// 替换原错误断言 const updateEvents = wrapper.emitted('update:modelValue') expect(updateEvents?.[0][0]).toBe(false)
方案2:直接访问组件内部的show ref
show是控制Toast显示状态的核心ref,通过组件的内部状态获取它并断言值:
// 替换原错误断言 const showRef = wrapper.vm.$.setupState.show expect(showRef.value).toBe(false)
方案3:模拟父组件更新props
如果一定要断言modelValue props的值,需要监听update:modelValue事件并手动更新父组件props:
it('Toast Props', async () => { const wrapper = shallowMount(Toast, { propsData: props }) // 监听更新事件,同步更新父组件props wrapper.vm.$on('update:modelValue', (newValue) => { wrapper.setProps({ modelValue: newValue }) }) await wrapper.setProps({ modelValue: true }) vi.runAllTimers() await wrapper.vm.$nextTick() expect(wrapper.emitted()).toHaveProperty('update:modelValue') expect(wrapper.emitted('update:modelValue')).toHaveLength(1) // 此时断言modelValue会成功 expect(wrapper.vm.modelValue).toBe(false) })
内容的提问来源于stack exchange,提问作者toimc
相关产品推荐
相关产品推荐

