Vitest 1.0 正式發(fā)布:Vue 團(tuán)隊打造的下一代測試框架!
12 月 5 日,由 Vue 團(tuán)隊打造的下一代測試框架 Vitest 正式推出 1.0 版本!
Vitest 于 2021 年 12 月推出,至今已經(jīng)過去了兩年的時間,其發(fā)展速度可謂是非常迅速,目前在 npm 下載量已經(jīng)達(dá)到了每周 250w,并且還在呈現(xiàn)快速增長的趨勢,Github Star 數(shù)量達(dá)到了 10.7k。
Vitest 是什么?
Vitest 是一個原生支持 Vite 的測試框架。Vitest 以其快速、簡潔的測試解決方案脫穎而出,需要最少的配置。Vitest 與廣泛采用的JavaScript測試框架 Jest 完美契合,并能無縫集成到 Vue 應(yīng)用中。雖然它專為與 Vite 一起使用而設(shè)計,但Vitest也可以獨立運行,在應(yīng)用中提供了靈活性。由于 Vitest 和 Vite 使用相同的配置文件,因此將 Vitest 集成到 Vue 應(yīng)用中很簡單。
Vitest 的主要功能如下:
- 與 Vite 通用的配置、轉(zhuǎn)換器、解析器和插件。
- 使用與你的應(yīng)用程序相同的設(shè)置來運行測試!
- 智能文件監(jiān)聽模式,就像是測試的 HMR!
- 支持對 Vue、React、Svelte、Lit 等框架進(jìn)行組件測試。
- 開箱即用的 TypeScript / JSX 支持
- ESM 優(yōu)先,支持模塊頂級 await
- 通過 Tinypool 使用 Worker 線程盡可能多地并發(fā)運行
- 使用 Tinybench 來支持基準(zhǔn)測試
- 套件和測試的過濾、超時、并發(fā)配置
- 支持 Workspace
- Jest 的快照功能
- 內(nèi)置 Chai 進(jìn)行斷言 + 與 Jest expect 語法兼容的 API
- 內(nèi)置用于對象模擬(Mock)的 Tinyspy
- 使用 jsdom 或 happy-dom 用于 DOM 模擬
- 通過 v8 或 istanbul 來輸出代碼測試覆蓋率
- 類似于 Rust 語言的源碼內(nèi)聯(lián)測試
- 通過 expect-type 進(jìn)行類型測試
Vitest 1.0 更新內(nèi)容
重大變更
- 添加對pool和poolOptions的支持,移除舊標(biāo)志
- 支持多個并行child_process
- 使快照更加美觀
- 為子包設(shè)置vitest對等依賴范圍
- 將最低 node 版本提升至 18,并匹配 Vite 5 的要求
- 移除已棄用的 node 加載器
- 將瀏覽器提供程序移到 @vitest/browser 包中
- 移除EnhancedSpy類型,棄用SpyInstance,改進(jìn)模擬和 vi 文檔
- runner: 正確處理自定義任務(wù),更新 runner hooks 命名
- coverage:
- 基于 glob 的覆蓋率閾值
- 使用transformMode和基于工作區(qū)項目的源映射
- 默認(rèn)啟用 coverage.all
新功能
- 添加 Marko 示例,并包括 Marko 文件的代碼覆蓋率
- 更新magic-string
- 實現(xiàn)provide/inject API 以傳輸來自主線程的數(shù)據(jù)
- 改進(jìn)expectTypeOf錯誤消息
- 添加test.sequential() api
- 允許自定義池
- 添加 --project 選項以限制正在運行的項目
- benchmark:將importTinybench移動到runner中
- browser:支持 "none" 提供程序,并更新lit示例以使用它
- coverage:支持 /* v8 ignore... 忽略提示
- expect:
- 支持 expect.closeTo api
- ToContain可以處理classList和Node.contains
- 通過href比較 URL 對象
- snapshot:添加配置快照目錄的選項
- vite-node:支持 Vite 5 的import.meta.hot.off
- vitest:
- 暴露getBenchFn、getBenchOptions
- 在測試期間運行類型檢查
- 過濾堆棧跟蹤
- 將execArgv暴露給不同的池
Vitest 嘗鮮
準(zhǔn)備工作
要想在 Vue 項目中運行自動化組件測試,首先要使用以下命令初始化一個 Vue 應(yīng)用:
npm create vite@latest vue-app --template vue
cd vue-app
npm install
項目安裝完成后,運行以下命令啟動應(yīng)用:
npm run dev
在瀏覽器中打開 http://127.0.0.1:5173/,即可看到應(yīng)用程序成功運行。
接下來,使用以下命令來安裝 Vitest:
npm install -D vitest
安裝完成后,我們需要將 Vitest 添加到 package.json 文件中。在 package.json 文件中添加測試腳本:
"scripts": {
// ...
"test": "vitest",
},
接下來,打開 vitest.config.js 文件并添加以下代碼:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
test:{
globals:true,
}
})
將 globals 屬性設(shè)置為 true 將允許在測試文件中訪問 Vitest API,而無需導(dǎo)入它們。
Test Utils 是一個 Vue 測試庫,提供了安裝 Vue 組件并與之交互的方法。使用以下命令安裝測試實用程序:
npm install --save-dev @vue/test-utils
我們應(yīng)該能夠在組件測試中模擬 DOM API。Vitest 目前支持happy-dom和jsdom。這里我們將使用 happy-dom。運行下面的命令來安裝happy-dom:
npm install happy-dom@6.0.4
安裝完成后,在 package.json 文件中的測試腳本中添加--dom
:
"scripts": {
// …
"test": "vitest --dom",
},
另外,我們需要將 happy-dom 添加到 vite.config.js 文件中,以使其在測試文件中全局可用:
test:{
// …
environment: 'happy-dom',
}
創(chuàng)建 Vue 組件
接下來,創(chuàng)建一個名為 GuessAge.vue 的簡單組件,該組件使用戶能夠輸入自己的姓名,并使用 Agify.io API 根據(jù)輸入的姓名猜測用戶的年齡。
在 src/components 文件夾中,創(chuàng)建一個 GuessAge.vue 文件并添加以下代碼:
<template>
<h1>{{ title }}</h1>
<div class="card">
<div style="width:400px;height:130px;margin-top:20px;border-style: dotted;" >
<br>
<span>名字: {{firstname}}</span> <br>
<span>年齡: {{age}}</span> <br>
</div><br><br>
<label> 輸入名字 </label><br>
<input type="text" v-model="search" style="font-size:20px;border-radius:10px;" placeholder=" Name ..."> <br> <br>
<button type="button" @click="getAge">猜年齡</button>
<br> <br> <br>
<input type="radio" value="pop"> <label>保存</label>
</div>
</template>
<script setup>
import { ref } from 'vue'
defineProps({
title: String
})
</script>
<script>
export default {
data() {
return {
search:"",
firstname:"",
age:"",
}
},
computed: {
getAge() {
fetch('https://api.agify.io/?name='+ this.search)
.then(response => response.json())
.then(data => {
this.age = data.age
this.firstname = data.name
this.search=""
})
}
}
}
</script>
測試 Vue 組件的 props 和函數(shù)
現(xiàn)在,我們需要為組件創(chuàng)建一個測試文件。根據(jù)命名約定,測試文件名必須以組件名稱開頭,以 .spec.js 或 .test.js 結(jié)尾。當(dāng)測試多個組件時,每個組件都應(yīng)該有一個測試文件。
在 Components 文件夾中創(chuàng)建一個名為 GuessAge.spec.js 的測試文件。該文件將包含 GuessAge 組件的簡單測試腳本。讓我們測試 GuessAge 組件,看看它在安裝時是否接收到正確的 props??梢酝ㄟ^將以下代碼添加到 GuessAge.spec.js 文件來測試掛載時 title 屬性的值:
import {mount} from "@vue/test-utils";
import GuessAge from "../components/GuessAge.vue";
// import { expect, test } from "vitest";
const wrapper = mount(GuessAge);
it("testing GuessAge component props", async () => {
expect(GuessAge.props.title).toContain("Guess User Age App");
});
從 @vue/test-utils 導(dǎo)入 mount,允許將組件包裝到一個名為 Wrapper 的特殊對象中,它提供了各種測試選項。
注意:如果在 Vite 的配置文件中將globals的值設(shè)置為false,那么在測試文件中就應(yīng)該添加 import { expect, test } from "vitest"。
運行以下命令以在監(jiān)聽模式下測試組件:
npm run test
我們還可以使用 toBe('function') 斷言方法來檢查應(yīng)用中是否存在函數(shù),如下所示:
it("Test if data is a function", () => {
expect(typeof GuessAge.data).toBe("function");
});
可以在終端中看到以下內(nèi)容:
使用快照測試用例
快照用于跟蹤用戶界面的變化。典型的快照測試用例會渲染一個UI組件,獲取一個快照,并將其與測試文件旁邊的參考快照文件進(jìn)行比較。它將當(dāng)前的UI狀態(tài)與已建立的快照進(jìn)行比較。如果當(dāng)前狀態(tài)與已建立狀態(tài)不匹配,測試將失敗。
要運行快照測試并跟蹤UI的變化,需要在GuessAge.spec.js測試文件中添加以下代碼:
test('snapshot UI testing', () => {
const wrapper = mount(GuessAge,{});
expect(wrapper.text()).toMatchSnapshot()
})
由于Vitest支持熱模塊重載,每次修改測試文件時都不必運行測試命令??梢栽诮K端中看到以下內(nèi)容:
模擬 HTTP 請求
在使用HTTP請求測試Vue組件時,我們首先需要模擬網(wǎng)絡(luò)請求;否則,測試將失敗。使用 Mock Service Worker (MSW) 進(jìn)行模擬可以輕松地通過攔截測試中的請求來測試 HTTP 請求,而不需要更改任何應(yīng)用程序代碼。
使用以下命令來安裝 MSW:
npm install msw --save-dev
我們需要在 GuessAge.spec.js 測試文件中導(dǎo)入以下兩個依賴項才能使用 MSW:
import { setupServer } from 'msw/node'
import { rest } from 'msw'
通過在GuessAge.spec.js測試文件中添加以下代碼來創(chuàng)建攔截HTTP請求的模擬服務(wù)器實例:
export const restHandlers = [
rest.get('https://api.agify.io/', (req, res, ctx) => {
return res(ctx.status(200), ctx.json([
{
age: 55,
name: "tope"
}
]))
}),
]
const server = setupServer(...restHandlers)
// 在所有測試之前啟動服務(wù)
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
// 在所有測試之后關(guān)閉服務(wù)
afterAll(() => server.close())
// 在每個測試后重置處理程序,對于測試隔離很重要
afterEach(() => server.resetHandlers())
測試點擊事件
接下來檢查應(yīng)用中是否存在按鈕,以及單擊“猜年齡”按鈕是否會在獲取用戶年齡后清除輸入標(biāo)簽
test("有按鈕", () => {
expect(wrapper.find("button").exists()).toBe(true);
});
test("Button clicked", async () => {
const ac = await wrapper.get("button").trigger("click")
expect(wrapper.vm.search).toEqual("")
})
可以在終端中看到以下內(nèi)容:
覆蓋率測試
為了評估代碼的效果和質(zhì)量,Vitest支持通過c8和Istanbul實現(xiàn)本地代碼覆蓋率,從而對代碼性能進(jìn)行報告和分析。
為了配置和運行覆蓋率測試,我們需要在vite.configure.js文件中添加以下內(nèi)容:
export default defineConfig({
plugins: [vue()],
test:{
globals:true,
coverage: {
provider: 'istanbul'
},
environment: 'happy-dom',
}
})
此外,我們還需要在package.json文件中的腳本部分添加coverage,如下所示:
"scripts": {
// ...
"coverage": "vitest run --coverage"
},
配置完成之后 ,接下來就使用以下命令安裝 Istanbul:
npm i -D @vitest/coverage-istanbul
當(dāng)執(zhí)行以下命令時,測試文件將會運行,Vitest 將在終端上顯示覆蓋率報告:
npm run coverage
測試時間
在運行測試時,確保測試在合理的時間內(nèi)完成非常重要。有時,一個測試可能需要太長時間才能完成,這可能會導(dǎo)致問題并延遲測試套件的運行。為了解決這個問題,Vitest提供了一個test-timeout選項,允許為測試設(shè)置超時時間。該選項指定了測試在超過最大時間限制之前可以完成的最長時間,否則將失敗并顯示超時錯誤。
可以使用以下命令為所有測試設(shè)置超時時間并替換默認(rèn)時間:
npx vitest run --test-timeout=50000
該命令會將 Vitest 測試框架中測試的超時值設(shè)置為 50 秒,可以根據(jù)測試要求將此值替換為不同的時間值。
從 Jest 遷移到 Vitest
將應(yīng)用的測試從 Jest 遷移到 itest 非常簡單。兩個測試框架使用類似的API,因此在遷移到 Vitest 時,可能不需要對現(xiàn)有的代碼進(jìn)行重大更改。下面就來看看如何從 Jest 遷移到 Vitest。
首先,我們需要從項目中刪除 Jest,并通過運行以下命令安裝 Vitest 以及所有必要的依賴項:
npm uninstall jest --save-dev
npm install vitest --save-dev
npm install --save-dev @vue/test-utils
接下來,我們需要在package.json文件中更新測試腳本,以使用Vitest而不是Jest??梢酝ㄟ^將測試腳本更改為Vitest來實現(xiàn):
"scripts": {
"test": "vitest",
}
現(xiàn)在,運行以下命令來測試應(yīng)用:
npm run test
如果在 Jest 測試代碼中使用了模塊模擬,需要將其更新為以下 Vitest 測試代碼:
// Jest
jest.mock('./module-path', () => 'hello')
// Vitest
vi.mock('./module-path', () => ({
default: 'hello'
}))