前端Vuer,请收好这份《Vue组件单元测试》宝典,给自己多一些安全感
大家好,我是 Kagol。
作为一名前端,在做业务开发的过程中,你是否曾经:
- 因为担心上线之后出bug,而反复手工验证自己负责的模块
- 不敢修改现有的“屎山”(别人写的或者是自己1年前写的)代码,从而不断地编写if/else
- 发现业务中有很多重复的代码,每次一改好多地方都要改,但又不敢重构,担心重构之后出bug
- 战战兢兢地修复一个陈年bug,就怕引起N个新bug
如果你中了以上任何一条,说明你现在缺乏安全感,你担心、你害怕、你不敢、你战战兢兢、你如履薄冰。
每天写代码都处在担惊受怕当中,程序员的尊严何在!
程序员的安全感要自己给自己,是时候改变现状了!
我们有很多方法可以给自己安全感,比如:
- 配置代码检测工具 ESLint
- 安装拼写检查插件 Code Spell Checker
- 代码评审 Code Review
- 结对编程 Pair programming
- 编写单元测试 Unit Test
本文主要给大家介绍如何在 Vue 项目中编写单元测试。
1 搭环境
我们的单元测试环境基于 vitest + @vue/test-utils。
前提:你需要有一个 Vue 项目,没有的话可以参考 Vite 官网进行创建。
第一步,在你的 Vue 项目中安装必要的依赖包。
npm i -D vitest @vue/test-utils happy-dom @vitest/coverage-v8
在 vite.config.ts 文件中增加以下配置。
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
// 新增
test: {
environment: 'happy-dom'
}
})
在 package.json 文件中增加相应的脚本命令
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview",
// 新增
"test": "vitest"
},
我们尝试执行以下命令:
npm test
会发现控制台会打印以下日志:
$ npm test
> vue3-vite@0.0.0 test
> vitest
DEV v0.33.0 /vue3-vite-demo
include: **/*.{test,spec}.?(c|m)[jt]s?(x)
exclude: **/node_modules/**, **/dist/**, **/cypress/**, **/.{idea,git,cache,output,temp}/**, **/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*
watch exclude: **/node_modules/**, **/dist/**
No test files found, exiting with code 1
意思是:没有找到单元测试文件。
除此之外,我们还能获取一些额外的信息,比如 include 表明了单元测试文件的命令格式:
**/*.{test,spec}.?(c|m)[jt]s?(x)
我们在 src/components 目录下创建一个 HelloWorld.spec.ts 文件。
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import HelloWorld from './HelloWorld.vue'
describe('测试 HelloWorld 组件', () => {
it('测试基本功能', async () => {
const wrapper = mount(HelloWorld)
expect(wrapper.exists()).toBeTruthy()
})
})
再次执行 npm test 命令
$ npm test
✓ src/components/HelloWorld.spec.ts (1)
✓ 测试 HelloWorld 组件 (1)
✓ 测试基本功能
Test Files 1 passed (1)
Tests 1 passed (1)
Start at 23:34:49
Duration 126ms
会提示:有一个单元测试用例通过。
这样我们的 Vue 项目单元测试环境就搭建成功,并且完成了第一个 Vue 单元测试用例
2 测什么
有了单元测试环境,我们可以在其中任意发挥,给自己满满的安全感。
那么我们应该如何给 Vue 组件编写单元测试呢?
我们以 OpenTiny Vue 的 DatePicker 组件为例。
效果图:
我们可以按照以下步骤编写单元测试用例:
Step 1:测试默认行为
- 测试基本功能:点击日期输入框,应该弹出日期选择面板,点击日期选择面板中的日期,面板消失,并在输入框中显示选中的日期
- 测试清除日期:鼠标移到已经选择日期的输入框中,应该出现清除日期的图标按钮,点击清除日期图标,输入框中的日期被清除
- ...
Step 2:测试每一个单独的 API
- 测试 disabled 属性:设置 disabled 属性之后,日期输入框应该变成禁用状态,点击输入框,不会弹出日期选择面板
- 测试 clearable 属性:设置 clearable 为 false 之后,鼠标移到输入框上,不显示清除日期的图标
- ...
Steps 3:测试 API 的边界值
- 测试日期越界:设置 v-model 的值为
"2023-07-32",应该不显示日期 - 测试错误格式的日期:设置 v-model 的值为数值
20230713,应该不显示日期 - ...
Steps 4:测试 API 之间的组合
- 测试 type/format 三个属性之间的组合:设置
type="week"和format="yyyy 年第 WW 周",日期选择面板应该变成选择周,选择5月7日到13日这周之后,输入框中显示的内容应该是2023 年第19周 - ...
<tiny-date-picker
v-model="value"
type="week"
format="yyyy 年第 WW 周"
></tiny-date-picker>
3 怎么测
单测三大件:describe / test / expect
一个单元测试包含三个部分:
- describe 测试套,里面可以包含多个测试用例,
- it(test) 测试用例
- expect 断言语句
import { describe, it, expect } from 'vitest'
describe('测试 HelloWorld 组件', () => {
it('测试基本功能', async () => {
const wrapper = mount(HelloWorld)
wrapper.
expect(wrapper.exists()).toBeTruthy()
expect(wrapper.find('.card').exists()).toBeTruthy()
})
it('测试 msg 属性', () => { ... })
})
模拟 Vue 组件挂载:mount 和 wrapper
每个单元测试里面测试 Vue 组件的表现是否正常,需要借助 @vue/test-utils 的 mount 方法,模拟组件的挂载。
import { mount } from '@vue/test-utils'
import HelloWorld from './HelloWorld.vue'
const wrapper = mount(HelloWorld, {
props: {
msg: 'OpenTiny'
}
})
用 mount 函数包裹组件,得到的是一个挂载好的组件对象,该对象包含了一系列实用的方法可以用于组件的测试。
比较常用的有:
- find:寻找组件内部的 DOM 节点
- findComponent:寻找子组件
- exists:判断组件或元素是否存在
- attributes:获取 DOM 节点的属性
- classes:获取 DOM 节点的 class
- ...
更多 wrapper 方法请参考:Vue Test Utils 官网文档
写单测就是写断言
有了 wrapper,就可以对 Vue 组件做断言。
比如以下断言用于判断 DOM 节点 .card 是否存在,toBeTruthy()用于断言一个值是否为 true。
expect(wrapper.find('.card').exists()).toBeTruthy()
常见的断言类型有:
- toBe:用于断言原始类型是否相等,比如:
expect(1+1).toBe(2) - toBeTruthy:用于断言一个值是否为 true
- not:用于否定断言,比如:
expect(1+1).toBe(3) - toBeGreaterThan:断言实际值是否大于接收到的值
- toEqual:断言实际值是否等于接收到的值或具有相同的结构(如果是对象,则递归比较它们),注意和
toBe的区别 - toContain:断言实际值是否在数组中
- toMatch:断言字符串是否与正则表达式或字符串匹配
- toHaveBeenCalled:用于测试函数是否已被调用
更多断言类型请参考:Vitest 官网
一个 DatePicker 组件的小例子
测试 DatePicker 组件的基本功能:点击日期输入框,应该弹出日期选择面板。
it('测试 DatePicker 组件的基本功能', async () => {
const value = ''
const wrapper = mount(() => <DatePicker v-model={value}></DatePicker>)
expect(wrapper.exists()).toBe(true)
// 没有点击日期输入框之前,没有日期选择面板
expect(document.querySelector('.tiny-date-picker')).not.toBeTruthy()
await wrapper.find('input').trigger('focus')
// 点击日期输入框之后,出现日期选择面板
expect(document.querySelector('.tiny-date-picker')).toBeTruthy()
})
3 自己动手实践吧
OpenTiny Vue 正在招募社区贡献者,欢迎加入我们
你可以通过以下方式参与贡献:
你可以根据自己的喜好认领以下类型的任务:
如何贡献单元测试:
- 在
packages/vue目录下搜索it.todo关键字,找到待补充的单元测试 - 按照以上指南编写组件单元测试
- 执行单个组件的单元测试:
pnpm test:unit3 button
如果你是一位经验丰富的开发者,想接受一些有挑战的任务,可以考虑以下任务:
- [Feature]: tree树形控件能增加虚拟滚动功能
- [Feature]: Tree 组件增加节点连接线
- [Feature]: 增加视频播放组件
- [Feature]: 增加思维导图组件
- [Feature]: 添加类似飞书的多维表格组件
- [Feature]: 添加到 unplugin-vue-components
- [Feature]: 兼容formily
参与 OpenTiny 开源社区贡献,你将收获:
直接的价值:
- 通过参与一个实际的跨端、跨框架组件库项目,学习最新的
Vite+Vue3+TypeScript+Vitest技术 - 学习从 0 到 1 搭建一个自己的组件库的整套流程和方法论,包括组件库工程化、组件的设计和开发等
- 为自己的简历和职业生涯添彩,参与过优秀的开源项目,这本身就是受面试官青睐的亮点
- 结识一群优秀的、热爱学习、热爱开源的小伙伴,大家一起打造一个伟大的产品
长远的价值:
- 打造个人品牌,提升个人影响力
- 培养良好的编码习惯
- 获得华为云 OpenTiny 团队的荣誉和定制小礼物
- 受邀参加各类技术大会
- 成为 PMC 和 Committer 之后还能参与 OpenTiny 整个开源生态的决策和长远规划,培养自己的管理和规划能力
- 未来有更多机会和可能
关于 OpenTiny
OpenTiny 是一套华为云出品的企业级组件库解决方案,适配 PC 端 / 移动端等多端,涵盖 Vue2 / Vue3 / Angular 多技术栈,拥有主题配置系统 / 中后台模板 / CLI 命令行等效率提升工具,可帮助开发者高效开发 Web 应用。
核心亮点:
跨端跨框架:使用 Renderless 无渲染组件设计架构,实现了一套代码同时支持 Vue2 / Vue3,PC / Mobile 端,并支持函数级别的逻辑定制和全模板替换,灵活性好、二次开发能力强。组件丰富:PC 端有80+组件,移动端有30+组件,包含高频组件 Table、Tree、Select 等,内置虚拟滚动,保证大数据场景下的流畅体验,除了业界常见组件之外,我们还提供了一些独有的特色组件,如:Split 面板分割器、IpAddress IP地址输入框、Calendar 日历、Crop 图片裁切等配置式组件:组件支持模板式和配置式两种使用方式,适合低代码平台,目前团队已经将 OpenTiny 集成到内部的低代码平台,针对低码平台做了大量优化周边生态齐全:提供了基于 Angular + TypeScript 的 TinyNG 组件库,提供包含 10+ 实用功能、20+ 典型页面的 TinyPro 中后台模板,提供覆盖前端开发全流程的 TinyCLI 工程化工具,提供强大的在线主题配置平台 TinyTheme
欢迎加入 OpenTiny 开源社区。
添加微信小助手:opentiny-official,一起参与共建!
OpenTiny 官网:https://opentiny.design/
Vue组件库:https://opentiny.design/tiny-vue
Angular组件库:https://opentiny.design/tiny-ng
OpenTiny 代码仓库:https://github.com/opentiny/ (欢迎 Star )
往期文章推荐
- OpenTiny Vue 3.9.0 版本发布:新增3个新组件、支持 SSR
- OpenTiny 3.8.0 正式发布:推出「极客黑」新主题!
- 使用 TinyCLI 两行命令创建一个美观大气的 Admin 系统
- 一个 OpenTiny,Vue2 Vue3 都支持!
- 历史性的时刻!OpenTiny 跨端、跨框架组件库正式升级 TypeScript,10 万行代码重获新生!
参考:
前端Vuer,请收好这份《Vue组件单元测试》宝典,给自己多一些安全感的更多相关文章
- 前端性能优化成神之路--vue组件懒加载(Vue Lazy Component )
---恢复内容开始--- 使用组件懒加载的原因 我们先来看看这样的一个页面,页面由大量模块组成,所有模块是同时进行加载,模块中图片内容较多,每个模块的依赖资源较多(包括js文件.接口文件.css文件等 ...
- 请收好这份NLP热门词汇解读
文章发布于公号[数智物语] (ID:decision_engine),关注公号不错过每一篇干货. 来源 | 微软研究院AI头条 编者按:在过去的一段时间,自然语言处理领域取得了许多重要的进展,Tran ...
- 干货 | 请收下这份2018学习清单:150个最好的机器学习,NLP和Python教程
机器学习的发展可以追溯到1959年,有着丰富的历史.这个领域也正在以前所未有的速度进化.在之前的一篇文章中,我们讨论过为什么通用人工智能领域即将要爆发.有兴趣入坑ML的小伙伴不要拖延了,时不我待! 在 ...
- 前端 | 页面触底自动加载 Vue 组件
不管是 web 端还是移动端,信息流都是现在很流行的信息展示方式.信息流经常搭配自动加载一起使用以获得更好的使用体验. 最近在使用 Vue 开发过程中也遇到了首页信息流自动加载的需求.大致了解了一下几 ...
- 参与开源之夏 x OpenTiny 跨端跨框架 UI 组件库贡献,可以赢取奖金🏆!这份《OpenTiny 开源贡献指南》请收好🎁!
大家好,我是 Kagol. 近期有几位朋友在 OpenTiny 技术交流群里询问我们在开源之夏(OSPP)的项目,希望能提前做一些准备工作. 这里给大家简单介绍下开源之夏. 开源之夏是由中科院软件所& ...
- python 全栈开发,Day90(Vue组件,前端开发工具包)
昨日内容回顾 1. Vue使用 1. 生成Vue实例和DOM中元素绑定 2. app.$el --> 取出该vue实例绑定的DOM标签 3. app.$data --> 取出该vue实例绑 ...
- 阿里oss上传图片react组件alioss-react,vue组件alioss-vue (不用我先收藏着,后端看下前端处理方法)
1.介绍 最近开发了一个项目,其中需要一个上传图片到阿里云的 oss 上面,就是上传图片到阿里云的 oss 上面. 因为之前开发过 vue 的阿里云 oss 上传,所以直接复制粘 vue 的组件. 因 ...
- 4.VUE前端框架学习记录四:Vue组件化编码2
VUE前端框架学习记录四:Vue组件化编码2文字信息没办法描述清楚,主要看编码Demo里面,有附带完整的代码下载地址,有需要的同学到脑图里面自取.脑图地址http://naotu.baidu.com/ ...
- 3.VUE前端框架学习记录三:Vue组件化编码1
VUE前端框架学习记录三:Vue组件化编码1文字信息没办法描述清楚,主要看编码Demo里面,有附带完整的代码下载地址,有需要的同学到脑图里面自取.脑图地址http://naotu.baidu.com/ ...
- 毕业季offer怎么拿?收下这份非典型求职面试指南
摘要:求职面试莫慌,先自我评估一下 ,华为云专家手把手为你指导. 本文分享自华为云社区<毕业季offer怎么拿?收下这份非典型求职面试指南>,原文作者:技术火炬手 . 又是一年毕业季,对于 ...
随机推荐
- TS(二)内置对象与class类
内置对象 ECMA内置对象 Boolean.Number.String.RegExp.Date.Error const regexp:RegExp = /\w\d\s const number:Num ...
- 帝国cms sql检则标题重复
<?php //检则标题重复 $sql=$empire->query("select id,filename from phome_ecms_news where classid ...
- 无法使用Resource注解
问题描述: 学习Spring框架的时候,发现无法使用@Resource注解,只能使用@Autowired注解. 问题原因: JDK11删除了javax.annotation包,需要导入,否则无法使用@ ...
- k8s介绍与常用命令
kubernetes基础与常用命令 原文地址 https://blog.csdn.net/footless_bird/article/details/125798691 官方文档 https://ku ...
- ROS用hector创建地图
ROS用hector创建地图 连接小车 ssh clbrobot@clbrobot 激活树莓派 roslaunch clbrobot bringup.launch 打开hector_slam 重新开终 ...
- 2021年蓝桥杯python真题-路径(数论+动态规划)(LCM、GCD和DP详细介绍)干货满满~
欢迎大家阅读本文章 如果大家对LCM和GCD不是很熟悉,这篇文章将对你有帮助! 本文章也会把动态规划做一定的介绍 题目: GCD和LCM的讲解: GCD的实现-辗转相除法: 在数学中,辗转相除法,又称 ...
- 省选联考2021vp记
卡牌游戏 考虑到将 \(a\) 和 \(b\) 放在一起排序,最后朝上的数字必然在左端点为最小值,右端点为最大值的区间中.这个区间中至少有 \(n-m\) 个是原来的 \(a\),且对于每张卡牌必然要 ...
- Eclipse 没有创建 Maven 项目入口的原因
试错过程 软件下载错了 我首先参考了 Eclipse历史版本下载和选择对应的java版本 这篇文章,发现 Eclipse 2020-06 的版本以后就不支持 JDK 8 了. 我想要下载 Oxygen ...
- Python_15 ddt驱动与日志
一.查缺补漏 1. 在测试报告中添加注释,写在类名下面就行,方法名下面,三引号 2. 直接import ddt引用的时候需要ddt.ddt, ddt.data, ddt.unpack from ddt ...
- #Python merge函数,pandas库数据查询功能,对标V-LOOKUP
日常办公中,我们经常会遇到需要匹配表,匹配对应数据的场景,在EXCEL中,我们习惯使用VLOOKUP函数或者是X-LOOKUP函数,今天学习的是Python,pandas库中的匹配功能. 首先导入所需 ...