Vue 3 组件测试实战:用 Vitest 与 @vue/test-utils 验证交互与逻辑
test12026-09-3011 次阅读
为什么给组件写测试
组件测试介于单元测试和端到端测试之间:它不依赖真实浏览器,只渲染单个组件并验证交互、props、emit 和渲染结果。相比 E2E 更快更稳,相比纯函数测试更贴近真实使用。
搭建测试环境
Vue 3 生态首选 Vitest,配合 @vue/test-utils 的 mount 与 shallowMount 来挂载组件。
npm install -D vitest @vue/test-utils jsdom
// vitest.config.ts
import { defineConfig } from "vitest/config";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [vue()],
test: {
environment: "jsdom",
globals: true,
},
});
测试一个计数器组件
先写一个最小计数器组件,点击按钮计数加一并抛出 change 事件,再针对它的行为进行断言。
<!-- Counter.vue -->
<script setup lang="ts">
import { ref } from "vue";
const count = ref(0);
const emit = defineEmits<{ change: [number] }>();
function inc() {
count.value++;
emit("change", count.value);
}
</script>
<template>
<button data-test="inc" @click="inc">次数:{{ count }}</button>
</template>
// counter.test.ts
import { describe, it, expect } from "vitest";
import { mount } from "@vue/test-utils";
import Counter from "@/components/Counter.vue";
describe("Counter", () => {
it("点击后计数 +1 并抛出 change 事件", async () => {
const wrapper = mount(Counter);
await wrapper.get('[data-test="inc"]').trigger("click");
expect(wrapper.text()).toContain("次数:1");
expect(wrapper.emitted("change")?.[0]).toEqual([1]);
});
});
测试异步渲染
组件里有接口请求时,用 flushPromises 等微任务跑完,再断言渲染结果。
import { flushPromises, mount } from "@vue/test-utils";
import UserCard from "@/components/UserCard.vue";
it("加载后渲染用户名", async () => {
const wrapper = mount(UserCard, {
props: { id: 1 },
});
await flushPromises();
expect(wrapper.text()).toContain("张三");
});
几个常用技巧
- 用 data-test 这类属性选择器定位元素,比依赖 class 更稳,重构样式也不影响测试。
- 触发事件用 trigger,返回 Promise,记得 await,否则断言会先于 DOM 更新执行。
- 只测行为不测实现:关注「点击后发生了什么」,而不是内部 ref 怎么变。
- 慢组件可以用 shallowMount 把子组件替成桩,隔离被测对象。
T
test1
文章作者
为什么给组件写测试 组件测试介于单元测试和端到端测试之间:它不依赖真实浏览器,只渲染单个组件并验证交互、props、e...
- 分类
- 技术
- 发布时间
- 2026-09-30
- 字数
- 约 1803 字
- 阅读
- 11 次
