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

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工具:

  1. 导入flushPromises:
import { flushPromises } from '@vue/test-utils';
  1. 修改测试代码,触发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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 07:55:04