前端Vuer,請收好這份《Vue組件單元測試》寶典,給自己多一些安全感
大家好,我是 Kagol。
作為一名前端,在做業(yè)務(wù)開發(fā)的過程中,你是否曾經(jīng):
因為擔(dān)心上線之后出bug,而反復(fù)手工驗證自己負(fù)責(zé)的模塊
不敢修改現(xiàn)有的“屎山”(別人寫的或者是自己1年前寫的)代碼,從而不斷地編寫if/else
發(fā)現(xiàn)業(yè)務(wù)中有很多重復(fù)的代碼,每次一改好多地方都要改,但又不敢重構(gòu),擔(dān)心重構(gòu)之后出bug
戰(zhàn)戰(zhàn)兢兢地修復(fù)一個陳年bug,就怕引起N個新bug
如果你中了以上任何一條,說明你現(xiàn)在缺乏安全感,你擔(dān)心、你害怕、你不敢、你戰(zhàn)戰(zhàn)兢兢、你如履薄冰。
每天寫代碼都處在擔(dān)驚受怕當(dāng)中,程序員的尊嚴(yán)何在!
程序員的安全感要自己給自己,是時候改變現(xiàn)狀了!
我們有很多方法可以給自己安全感,比如:
配置代碼檢測工具 ESLint
安裝拼寫檢查插件 Code Spell Checker
代碼評審 Code Review
結(jié)對編程 Pair programming
編寫單元測試 Unit Test
本文主要給大家介紹如何在 Vue 項目中編寫單元測試。
1 搭環(huán)境
我們的單元測試環(huán)境基于?vitest
?+?@vue/test-utils
。
前提:你需要有一個 Vue 項目,沒有的話可以參考?Vite?官網(wǎng)進行創(chuàng)建。
第一步,在你的 Vue 項目中安裝必要的依賴包。
shell
npm i -D vitest @vue/test-utils happy-dom @vitest/coverage-v8
在?vite.config.ts
?文件中增加以下配置。
ts
import { defineConfig } from 'vitest/config'import vue from '@vitejs/plugin-vue'export default defineConfig({ ?plugins: [vue()], ?// 新增 ?test: { ? ?environment: 'happy-dom' ?}})
在?package.json
?文件中增加相應(yīng)的腳本命令
?"scripts": { ? ?"dev": "vite", ? ?"build": "vue-tsc && vite build", ? ?"preview": "vite preview", ? ? ? ?// 新增 ? ?"test": "vitest" ?},
我們嘗試執(zhí)行以下命令:
npm test
會發(fā)現(xiàn)控制臺會打印以下日志:
$ npm test> vue3-vite@0.0.0 test> vitest DEV ?v0.33.0 /vue3-vite-demoinclude: **/*.{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
?目錄下創(chuàng)建一個?HelloWorld.spec.ts
?文件。
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() ?})})
再次執(zhí)行?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 項目單元測試環(huán)境就搭建成功,并且完成了第一個 Vue 單元測試用例????
2 測什么
有了單元測試環(huán)境,我們可以在其中任意發(fā)揮,給自己滿滿的安全感。
那么我們應(yīng)該如何給 Vue 組件編寫單元測試呢?
我們以 OpenTiny Vue 的?DatePicker?組件為例。
效果圖:

我們可以按照以下步驟編寫單元測試用例:
Step 1:測試默認(rèn)行為
測試基本功能:點擊日期輸入框,應(yīng)該彈出日期選擇面板,點擊日期選擇面板中的日期,面板消失,并在輸入框中顯示選中的日期
測試清除日期:鼠標(biāo)移到已經(jīng)選擇日期的輸入框中,應(yīng)該出現(xiàn)清除日期的圖標(biāo)按鈕,點擊清除日期圖標(biāo),輸入框中的日期被清除
...
Step 2:測試每一個單獨的 API
測試 disabled 屬性:設(shè)置 disabled 屬性之后,日期輸入框應(yīng)該變成禁用狀態(tài),點擊輸入框,不會彈出日期選擇面板
測試 clearable 屬性:設(shè)置 clearable 為 false 之后,鼠標(biāo)移到輸入框上,不顯示清除日期的圖標(biāo)
...
Steps 3:測試 API 的邊界值
測試日期越界:設(shè)置 v-model 的值為?
"2023-07-32"
,應(yīng)該不顯示日期測試錯誤格式的日期:設(shè)置 v-model 的值為數(shù)值?
20230713
,應(yīng)該不顯示日期...
Steps 4:測試 API 之間的組合
測試 type/format 三個屬性之間的組合:設(shè)置?
type="week"
?和?format="yyyy 年第 WW 周"
,日期選擇面板應(yīng)該變成選擇周,選擇5月7日到13日這周之后,輸入框中顯示的內(nèi)容應(yīng)該是?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
每個單元測試?yán)锩鏈y試 Vue 組件的表現(xiàn)是否正常,需要借助?@vue/test-utils
?的 mount 方法,模擬組件的掛載。
import { mount } from '@vue/test-utils'import HelloWorld from './HelloWorld.vue'const wrapper = mount(HelloWorld, { ?props: { ? ?msg: 'OpenTiny' ?}})
用 mount 函數(shù)包裹組件,得到的是一個掛載好的組件對象,該對象包含了一系列實用的方法可以用于組件的測試。

比較常用的有:
find:尋找組件內(nèi)部的 DOM 節(jié)點
findComponent:尋找子組件
exists:判斷組件或元素是否存在
attributes:獲取 DOM 節(jié)點的屬性
classes:獲取 DOM 節(jié)點的 class
...
更多 wrapper 方法請參考:Vue Test Utils 官網(wǎng)文檔
寫單測就是寫斷言
有了 wrapper,就可以對 Vue 組件做斷言。
比如以下斷言用于判斷 DOM 節(jié)點?.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:斷言實際值是否等于接收到的值或具有相同的結(jié)構(gòu)(如果是對象,則遞歸比較它們),注意和?
toBe
?的區(qū)別toContain:斷言實際值是否在數(shù)組中
toMatch:斷言字符串是否與正則表達式或字符串匹配
toHaveBeenCalled:用于測試函數(shù)是否已被調(diào)用
更多斷言類型請參考:Vitest 官網(wǎng)
一個 DatePicker 組件的小例子
測試 DatePicker 組件的基本功能:點擊日期輸入框,應(yīng)該彈出日期選擇面板。
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') ?// 點擊日期輸入框之后,出現(xiàn)日期選擇面板 ?expect(document.querySelector('.tiny-date-picker')).toBeTruthy()})
3 自己動手實踐吧
OpenTiny Vue 正在招募社區(qū)貢獻者,歡迎加入我們??
你可以通過以下方式參與貢獻:
在?issue?列表中選擇自己喜歡的任務(wù)
閱讀貢獻者指南,開始參與貢獻
你可以根據(jù)自己的喜好認(rèn)領(lǐng)以下類型的任務(wù):
編寫單元測試
修復(fù)組件缺陷
為組件添加新特性
完善組件的文檔
如何貢獻單元測試:
在
packages/vue
目錄下搜索it.todo
關(guān)鍵字,找到待補充的單元測試按照以上指南編寫組件單元測試
執(zhí)行單個組件的單元測試:
pnpm test:unit3 button
如果你是一位經(jīng)驗豐富的開發(fā)者,想接受一些有挑戰(zhàn)的任務(wù),可以考慮以下任務(wù):
??[Feature]: tree樹形控件能增加虛擬滾動功能
??[Feature]: Tree 組件增加節(jié)點連接線
??[Feature]: 增加視頻播放組件
??[Feature]: 增加思維導(dǎo)圖組件
??[Feature]: 添加類似飛書的多維表格組件
??[Feature]: 添加到 unplugin-vue-components
??[Feature]: 兼容formily
參與 OpenTiny 開源社區(qū)貢獻,你將收獲:
直接的價值:
通過參與一個實際的跨端、跨框架組件庫項目,學(xué)習(xí)最新的
Vite
+Vue3
+TypeScript
+Vitest
技術(shù)學(xué)習(xí)從 0 到 1 搭建一個自己的組件庫的整套流程和方法論,包括組件庫工程化、組件的設(shè)計和開發(fā)等
為自己的簡歷和職業(yè)生涯添彩,參與過優(yōu)秀的開源項目,這本身就是受面試官青睞的亮點
結(jié)識一群優(yōu)秀的、熱愛學(xué)習(xí)、熱愛開源的小伙伴,大家一起打造一個偉大的產(chǎn)品
長遠的價值:
打造個人品牌,提升個人影響力
培養(yǎng)良好的編碼習(xí)慣
獲得華為云 OpenTiny 團隊的榮譽和定制小禮物
受邀參加各類技術(shù)大會
成為 PMC 和 Committer 之后還能參與 OpenTiny 整個開源生態(tài)的決策和長遠規(guī)劃,培養(yǎng)自己的管理和規(guī)劃能力
未來有更多機會和可能
關(guān)于 OpenTiny
OpenTiny?是一套華為云出品的企業(yè)級組件庫解決方案,適配 PC 端 / 移動端等多端,涵蓋 Vue2 / Vue3 / Angular 多技術(shù)棧,擁有主題配置系統(tǒng) / 中后臺模板 / CLI 命令行等效率提升工具,可幫助開發(fā)者高效開發(fā) Web 應(yīng)用。
核心亮點:
跨端跨框架
:使用 Renderless 無渲染組件設(shè)計架構(gòu),實現(xiàn)了一套代碼同時支持 Vue2 / Vue3,PC / Mobile 端,并支持函數(shù)級別的邏輯定制和全模板替換,靈活性好、二次開發(fā)能力強。組件豐富
:PC 端有80+組件,移動端有30+組件,包含高頻組件 Table、Tree、Select 等,內(nèi)置虛擬滾動,保證大數(shù)據(jù)場景下的流暢體驗,除了業(yè)界常見組件之外,我們還提供了一些獨有的特色組件,如:Split 面板分割器、IpAddress IP地址輸入框、Calendar 日歷、Crop 圖片裁切等配置式組件
:組件支持模板式和配置式兩種使用方式,適合低代碼平臺,目前團隊已經(jīng)將 OpenTiny 集成到內(nèi)部的低代碼平臺,針對低碼平臺做了大量優(yōu)化周邊生態(tài)齊全
:提供了基于 Angular + TypeScript 的?TinyNG?組件庫,提供包含 10+ 實用功能、20+ 典型頁面的?TinyPro?中后臺模板,提供覆蓋前端開發(fā)全流程的 TinyCLI 工程化工具,提供強大的在線主題配置平臺?TinyTheme
歡迎加入 OpenTiny 開源社區(qū)。
添加微信小助手:opentiny-official,一起參與共建!
OpenTiny?官網(wǎng):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 版本發(fā)布:新增3個新組件、支持 SSR
??OpenTiny 3.8.0 正式發(fā)布:推出「極客黑」新主題!
??使用 TinyCLI 兩行命令創(chuàng)建一個美觀大氣的 Admin 系統(tǒng)
??一個 OpenTiny,Vue2 Vue3 都支持!
??歷史性的時刻!OpenTiny 跨端、跨框架組件庫正式升級 TypeScript,10 萬行代碼重獲新生!
參考:
Vitest 官網(wǎng)
Vue Test Utils 官網(wǎng)