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

如何使用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)
  })
})

问题原因

  1. Vue单向数据流约束:modelValue是父组件传入的props,Vue中props是只读的,子组件通过defineModel修改值时,本质是向父组件发送update:modelValue事件,不会自动修改子组件自身的modelValue props值。测试中wrapper.vm.modelValue获取的是父组件最初传入(或后续通过setProps设置)的props值,父组件未更新时,该值保持true。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 19:04:59