Vue3+Vuelidate异步验证后组件submit事件不触发的测试问题
Vue3 + Vuelidate2 单元测试中submit事件未被检测到
环境
- Vue 3.3.*
- Vuelidate 2.0.*
- 组合式API(setup)+ TypeScript + TailwindCSS
- 测试框架:Vitest
问题
编写单元测试验证表单点击提交按钮时触发submit事件,但发现只要在const isValid = await v$.value.$validate();之后调用emit('submit'),该事件就无法被测试检测到。已确认isValid为true,尝试添加await wrapper.vm.$nextTick()无效。
相关代码
测试代码
it("should emit submit event when submit button is clicked", async () => { const nameInput = wrapper.find("input#name"); const emailInput = wrapper.find("input#email"); const messageInput = wrapper.find("textarea#message"); // 填入合法数据 await nameInput.setValue("John Doe"); await emailInput.setValue("john.doe@example.com"); await messageInput.setValue("A 10+ character long message."); await wrapper.find("form").trigger("submit"); expect(wrapper.emitted()).toHaveProperty("submit"); });
表单模板
<form ref="formElement" id="contact-form" @submit.prevent="handleSubmit" class="flex flex-col gap-4"> <div class="flex flex-col"> <label for="name" class="text-lg mb-2">Name</label> <input type="text" id="name" name="name" @change="v$.name.$reset()" :class="inputCss + ' ' + (v$.name.$error ? 'border-red-500' : 'border-white')" /> <span v-if="v$.name.$error" class="ml-2 italic text-red-500">{{ v$.name.$errors[0].$message }}</span> </div> <div class="flex flex-col"> <label for="email" class="text-lg mb-2">Email</label> <input type="email" id="email" name="email" @change="v$.email.$reset()" :class="inputCss + ' ' + (v$.email.$error ? 'border-red-500' : 'border-white')" /> <span v-if="v$.email.$error" class="ml-2 italic text-red-500">{{ v$.email.$errors[0].$message }}</span> </div> <div class="flex flex-col"> <label for="message" class="text-lg mb-2">Message</label> <textarea id="message" name="message" @change="v$.message.$reset()" :class="inputCss + ' ' + (v$.message.$error ? 'border-red-500' : 'border-white')" ></textarea> <span v-if="v$.message.$error" class="ml-2 italic text-red-500">{{ v$.message.$errors[0].$message }}</span> </div> <div class="flex justify-center"> <button type="submit" class="hover:bg-gray-50 hover:text-gray-900 duration-75" :class="inputCss"> Submit </button> </div> </form>
handleSubmit函数
async function handleSubmit() { if (!formElement.value) return; const newFormData = new FormData(formElement.value); const data = Object.fromEntries(newFormData.entries()); Object.assign(formData, data); const isValid = await v$.value.$validate(); if (!isValid) return; // 此处emit无法被测试检测到 emit("submit", toRaw(formData)); console.log("Should have submitted now, but hasn't!") }
原因与解决方法
原因
Vuelidate的$validate()是异步操作,测试代码触发submit后立刻执行断言,此时handleSubmit里的异步验证流程还没走完,emit还没执行。$nextTick仅处理Vue的DOM更新周期,不会等待自定义异步函数完成。
解决方法
在断言前等待所有异步任务完成,推荐用Vitest提供的flushPromises工具:
- 导入
flushPromises:
import { flushPromises } from '@vue/test-utils';
- 修改测试代码,触发submit后添加
await flushPromises():
it("should emit submit event when submit button is clicked", async () => { const nameInput = wrapper.find("input#name"); const emailInput = wrapper.find("input#email"); const messageInput = wrapper.find("textarea#message"); await nameInput.setValue("John Doe"); await emailInput.setValue("john.doe@example.com"); await messageInput.setValue("A 10+ character long message."); await wrapper.find("form").trigger("submit"); // 等待所有异步操作(包括Vuelidate的验证)完成 await flushPromises(); expect(wrapper.emitted()).toHaveProperty("submit"); });
如果无法使用flushPromises,也可以用await new Promise(resolve => setTimeout(resolve, 0))替代,同样能等待微任务执行完毕。
内容的提问来源于stack exchange,提问作者Eric B
相关产品推荐
相关产品推荐

