- 作者:老汪软件技巧
- 发表时间:2024-12-02 21:06
- 浏览量:
Vitest
看标题,没错,本次测试用到的是Vitest,为什么选择Vitest而不是Jest,首先,如果看Vitest的文档是可以看到Vitest是兼容Jest的API,另外一个重要的原因就是,Vite和Vitest本来就可以看作一家人,只是分工不同,官方大大是这样说的:Vitest 旨在将自己定位为 Vite 项目的首选测试框架,即使对于不使用 Vite 的项目也是一个可靠的替代方案
为什么要测试
你可能会问,为什么我们要花时间和精力去学习和编写测试呢?尤其是在接触测试的时候,确实会让人感到复杂且难以掌握。毕竟,直接引入组件并手动测试,或者通过控制台打印来调试工具函数,似乎更简单直接。那么,为什么我们还需要专门学习如何编写自动化测试呢?
1.随着项目规模的增长,手动测试变得不切实际
当你刚开始开发一个简单的组件时,比如 Button.vue,属性不多,功能也不复杂,手动测试可能确实是一个可行的选择。你可以逐个检查每个属性的行为,确保一切正常。但是,随着项目的不断扩展,组件的属性越来越多,功能也变得更加复杂。此时,手动测试的效率会大大降低,甚至可能出现遗漏的情况。
2.自动化测试提高了开发效率
自动化测试可以大幅提高开发效率。通过编写测试用例,你可以让计算机自动执行这些测试,而不需要每次都手动点击、输入和检查结果。这不仅节省了时间,还能确保每次代码更改后,所有功能都能正常工作。
3.测试是代码质量的保障
编写测试不仅是为了解决当前的问题,更是为了确保代码的长期可维护性和稳定性。良好的测试覆盖率可以让你的代码更具健壮性,减少潜在的错误和漏洞。
4.测试帮助你更好地设计代码
编写测试的过程不仅仅是验证代码是否正确,它还可以帮助你更好地设计代码。通过编写测试,你可以更清晰地思考组件的接口、行为和边界条件。这种“测试驱动开发”(TDD)的方法可以让你写出更加简洁、易于维护的代码。
测试Button组件
下载Vitest
npm install -D vitest
我们这里使用的是Vue,所以还要下载基于Vue的测试工具,显然Vitest肯定不是只能测试基于Vite构建或使用Vue框架相关的项目
下载vue-test-utils
npm install -D @vue/test-utils
Vitest 配置
安装完 Vitest 后,根文件夹中添加vitest.config.js文件中:
/// >
// 上面这行是 TypeScript 的三斜杠指令,用于引用 `vitest` 的类型定义。它确保我们在编写测试时可以使用 Vitest 提供的全局 API(如 `describe`、`test`、`expect` 等),并且获得正确的类型提示。
import { defineConfig } from "vite";
// 从 Vite 中导入 `defineConfig` 函数,用于定义 Vite 的配置对象。Vite 是一个现代的构建工具,支持快速开发和高效的生产构建。
import vue from "@vitejs/plugin-vue";
// 导入 Vite 的 Vue 插件,该插件用于处理 `.vue` 文件,支持单文件组件(SFC)的编译和热更新。
import vueJsx from "@vitejs/plugin-vue-jsx";
// 导入 Vite 的 Vue JSX 插件,该插件允许在 Vue 组件中使用 JSX 语法,适用于需要 JSX 支持的项目。
import VueMacros from "unplugin-vue-macros";
// 导入 `unplugin-vue-macros`,这是一个增强 Vue 开发体验的插件。它提供了多个宏(如 `defineComponent`、`setup` 等),简化了 Vue 组件的编写,并且与其他工具(如 Vite 和 Vue-Test-Utils)兼容。
// Vite 配置对象,用于定义项目的构建和开发环境设置
export default defineConfig({
plugins: [
// 使用 `VueMacros.vite` 来配置 Vite 插件链。`VueMacros` 是一个元插件,它可以自动加载并配置其他插件(如 `vue` 和 `vueJsx`),并且提供额外的功能。
VueMacros.vite({
plugins: {
// 配置 Vue 插件,处理 `.vue` 文件
vue: vue(),
// 配置 Vue JSX 插件,支持 JSX 语法
vueJsx: vueJsx(),
},
}),
],
test: {
globals: true, // 启用全局模式,使得 Vitest 的全局 API(如 `describe`、`test`、`expect` 等)可以直接在测试文件中使用,而不需要显式导入。
environment: "jsdom", // 指定测试环境为 `jsdom`,这是一个虚拟的 DOM 实现,适用于浏览器环境的测试。`jsdom` 可以模拟浏览器的行为,使得我们可以测试与 DOM 相关的功能,而不需要实际打开浏览器。
},
});
开始测试
// Button.test.ts
import { describe, test, expect } from "vitest";
import { mount } from "@vue/test-utils";
import Button from "./Button.vue";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import Icon from "../Icon/Icon.vue";
// 使用 vitest 的 describe 函数来定义测试套件,测试 Button.vue 组件
describe("Button.vue", () => {
// 测试基础按钮的功能
test("basic button", () => {
// 使用 mount 函数挂载 Button 组件,并传递 props 和 slots
const wrapper = mount(Button, {
props: {
type: "primary", // 设置按钮类型为 "primary"
},
slots: {
default: "button", // 设置默认插槽内容为 "button"
},
});
// 打印组件的 HTML 结构,用于调试
console.log(wrapper.html());
// 检查按钮是否具有预期的类名 "jd-button--primary"
expect(wrapper.classes()).toContain("jd-button--primary");
// 检查按钮的文本内容是否为 "button"
expect(wrapper.get("button").text()).toBe("button");
// 检查页面中只有一个按钮元素
expect(wrapper.findAll("button").length).toBe(1);
// 触发按钮的点击事件
wrapper.get("button").trigger("click");
// 检查按钮是否触发了 "click" 事件
expect(wrapper.emitted()).toHaveProperty("click");
});
// 测试禁用按钮的功能
test("disabled button", () => {
// 挂载 Button 组件,并设置 disabled 属性为 true
const wrapper = mount(Button, {
props: {
disabled: true,
},
slots: {
default: "button",
},
});
// 检查按钮是否具有 "disabled" 属性
expect(wrapper.get("button").attributes("disabled")).toBeDefined();
// 触发禁用按钮的点击事件
wrapper.get("button").trigger("click");
// 检查禁用按钮是否没有触发 "click" 事件
expect(wrapper.emitted()).not.toHaveProperty("click");
});
// 测试带有图标的按钮
test("icon", () => {
// 挂载 Button 组件,并设置 icon 属性为 "search"
const wrapper = mount(Button, {
props: {
icon: "search",
},
slots: {
default: "button",
},
global: {
stubs: ["FontAwesomeIcon"], // 模拟第三方组件 FontAwesomeIcon
},
});
// 检查按钮内部是否存在 FontAwesomeIcon 组件
const icon = wrapper.findComponent(FontAwesomeIcon);
expect(icon.exists()).toBe(true);
// 检查图标组件的 icon 属性是否为 "search"
expect(icon.props("icon")).toBe("search");
});
// 测试加载中的按钮
test("loading", () => {
// 挂载 Button 组件,并设置 loading 属性为 true
const wrapper = mount(Button, {
props: {
loading: true,
},
slots: {
default: "loading",
},
global: {
stubs: ["Icon"], // 模拟本地的 Icon 组件
},
});
// 打印组件的 HTML 结构,用于调试
console.log(wrapper.html());
// 检查按钮是否具有 "is-loading" 类名
expect(wrapper.get("button").classes()).toContain("is-loading");
// 检查按钮的文本内容是否为 "loading"
expect(wrapper.get("button").text()).toBe("loading");
// 检查按钮内部是否存在 Icon 组件
const icon = wrapper.findComponent(Icon);
expect(icon.exists()).toBe(true);
// 检查图标组件的 icon 属性是否为 "spinner"
expect(icon.props("icon")).toBe("spinner");
// 检查按钮是否具有 "disabled" 属性
expect(wrapper.attributes("disabled")).toBeDefined();
});
});
启动测试,Button就是测试代码所在的文件名 => Button.test.ts
npx vitest Button
测试结果如下:
可以看到生成了button节点且测试内容均生效
至于测试中使用到的函数及方法,这里简单介绍下:
describe:
test:
expect:
mount:
测试的重要性
所以看到这里应该知道测试的重要性了:
总结
使用 Vitest 对我们的应用程序进行单元测试是无缝的,与Jest等替代品相比,需要更少的步骤来启动和运行。Vitest 还可以很容易地将现有的测试从 Jest 迁移到Vitest,而不需要进行额外的配置。