Vue Test Utils v2:测试keydown事件下v-model值更新报错排查
Vue Test Utils + Vitest 测试v-model更新时遇
$emit报错问题 我是Vue Test Utils新手,用Vite搭配Vitest做测试,在测试自定义AutoComplete组件的v-model更新逻辑时卡壳了。这个组件有input和results两个插槽,input插槽监听@keydown事件:按下特殊按键会触发对应操作,普通按键则要更新输入框值和对应的modelValue。
简化后的AutoComplete组件代码
<script setup> import { ref, watchEffect, computed} from 'vue'; // Props const props = defineProps({ modelValue : { type: [String, null], required: true }, autoCompleteConfigs : { type: Array, required: true, } }); const emit = defineEmits(['selected','chosen','updated','update:modelValue']); const methods = { // 用户选择结果时的逻辑 onChosen : (result) =>{ /// 省略业务逻辑 }, moveSelection: (x) =>{ /// 省略业务逻辑 }, // 键盘按下事件处理 onKeydown : (event) => { // 省略判断逻辑 const passThruKeys = ['ArrowUp','ArrowDown','Tab','Enter','Delete']; if (passThruKeys.includes(event.key)) { event.preventDefault(); // 省略按键对应逻辑 } } }; // 组件内用于存储输入查询内容的ref const query = ref(''); </script> <template> <!-- input插槽 --> <div> <slot name="input" @keydown="methods.onKeydown" ></slot> </div> <!-- results插槽 --> <div> <slot v-if="searchResults.length > 0" name="results" :results="searchResults" :indexSearched="currentSearchIndex" @chosen="methods.onChosen"></slot> </div> </template>
我的测试代码
import { mount} from "@vue/test-utils"; import AutoComplete from '@/Components/AutoComplete.vue' import { describe, it, expect, vi} from "vitest"; describe("AutoComplete", () => { let wrapper; beforeEach(() => { wrapper = mount(AutoComplete, { props: { modelValue: 'Some text ', 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }), autoCompleteConfigs : [ { trigger : '#', index : 'tag', autoComplete : 'tag' }, { trigger : '@', index : 'user', autoComplete : 'username' } ], }, slots: { input: `<input :value="modelValue" @keydown="onKeydown" id="message" @input="$emit( 'update:modelValue', $event.target.value )"/>`, }, }); }); it("should update model value", async() => { const input = wrapper.find('#message'); input.setValue(wrapper.props().modelValue + "#h"); await wrapper.vm.$nextTick(); expect (wrapper.vm.query).toBe("h"); }); });
运行测试后报错
TypeError: _ctx.$emit is not a function
问题原因及修复方案
问题根源
- 插槽模板里的
$emit属于测试上下文的父级,无法直接调用AutoComplete组件的emit方法; - 测试里手动绑定
onUpdate:modelValue的写法不够规范; <script setup>里的query是组件私有变量,测试无法直接访问。
修复步骤
- 修正插槽的
$emit调用:
插槽input里的$emit改为调用组件实例的$emit,通过$parent访问(插槽内容挂载在组件内部,$parent指向AutoComplete实例)。 - 规范v-model事件监听:
单独通过listeners监听update:modelValue事件,更清晰跟踪值的变化。 - 暴露组件内部变量:
在AutoComplete组件里用defineExpose把query暴露出来,让测试能访问到。
修正后的测试代码
import { mount} from "@vue/test-utils"; import AutoComplete from '@/Components/AutoComplete.vue' import { describe, it, expect} from "vitest"; describe("AutoComplete", () => { let wrapper; let updatedModelValue; beforeEach(() => { updatedModelValue = ''; wrapper = mount(AutoComplete, { props: { modelValue: 'Some text ', autoCompleteConfigs : [ { trigger : '#', index : 'tag', autoComplete : 'tag' }, { trigger : '@', index : 'user', autoComplete : 'username' } ], }, slots: { input: `<input :value="modelValue" @keydown="onKeydown" id="message" @input="$parent.$emit('update:modelValue', $event.target.value)"/>`, }, listeners: { 'update:modelValue': (val) => { updatedModelValue = val; wrapper.setProps({ modelValue: val }); } } }); }); it("should update model value and query", async() => { const input = wrapper.find('#message'); const newInputValue = `${wrapper.props().modelValue}#h`; await input.setValue(newInputValue); // 断言modelValue更新正确 expect(updatedModelValue).toBe(newInputValue); // 断言组件内部query值正确(需组件暴露query) expect(wrapper.vm.query).toBe("h"); }); });
组件补充修改
在AutoComplete组件的<script setup>末尾添加defineExpose:
<script setup> // ... 原有代码 // 暴露内部变量给测试 defineExpose({ query }); </script>
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

