首先先建立Vite專案 Vite 安裝
在專案根目錄安裝vitest @vue/test-utils jsdom
建立專案時要加入單元測試必須在Node.js 版本必須為 14 以上;專案本身是由 Vite(2.7.10 版本以上)所建構的之外
1 2
| cd 到專案 npm install -D vitest @vue/test-utils jsdom
|
在專案根目錄安裝vitest @vue/test-utils jsdom
jsdom:是一個可以使用類似dom元件操作方式來操作html text的工具
在 ~專案根目錄/package.json執行腳本指令新增單元測試所需要的指令
scripts加入
“test”: “vitest –environment jsdom”,(啟動單元測試指令時環境內的jsdom)
1 2 3 4 5 6 7 8 9 10 11
| "scripts": { "dev": "vite", "build": "vue-tsc && vite build", "test": "vitest --environment jsdom", "preview": "vite preview" }, "devDependencies": { "@vue/test-utils": "^2.4.5", "jsdom": "^24.0.0", "vitest": "^1.5.3" }
|
元件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| //引入描述、它、期望 import { describe, it, expect } from 'vitest' //引入測試實用程式 import { mount } from '@vue/test-utils' //引入元件 import HelloWorld from '../HelloWorld.vue'
describe('HelloWorld', () => { //1+1 應該是2 it('1 + 1 should be 2', () => { expect(1 + 1).toBe(2) }) //正確渲染 it('renders properly', () => { const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } }) expect(wrapper.text()).toContain('Hello Vitest') }) //在此測試檔案中使用 jsdom it('use jsdom in this test file', () => { const element = document.createElement('div') element.innerHTML = '<p>Hello, HTML!</p>' expect(element.innerHTML).toBe('<p>Hello, HTML!</p>') }) })
|
執行啟動單元測試指令
單元測試設置,設定完整語法專案
新增src/test/config/新增vitest.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { defineConfig } from 'vitest/config' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [ vue(), ], test: { // 在這裡加入測試設定:.spec.js類型都執行單元測試 include: ['**/*.spec.js'], // 全域 globals: true,//就不需要顯示引入 vitest 測試相關的 API,讓測試看起來更乾淨 environment: 'jsdom', }, })
|
執行啟動單元測試指令
必須在 ~專案根目錄/package.json執行腳本指令新增單元測試所需要的指令,
改成剛剛設定的路徑
1 2 3
| "scripts": { "test": "vitest --config ./src/test/config/vitest.config.js", },
|
啟動單元測試指令
得到以下

github
單元測試覆蓋
Vitest 支援兩種程式碼覆蓋率引擎,v8 和 Istanbul。
v8 使用Chrome v8 引擎中內建的程式碼覆蓋率測量。
v8 的一大優點是不需要預先檢測和轉譯。
v8 無法區分 if 語句、三元條件或 for 迴圈中的條件。它將它們全部突出顯示為條件表達式。
Istanbul是一個歷史悠久的 JavaScript 測試覆蓋率工具。
Istanbul檢測需要使用 Babel 插件實現轉譯過程。幸運的是,一切都由 Vitest 為您處理。
Istanbul的一大優勢是偵測發生在單行程式碼的層級。您應該得到非常精確的結果。只有您感興趣的程式碼才會被偵測。
安裝@vitest/coverage-v8 與vitest/coverage-istanbul指令
1 2
| npm install -D @vitest/coverage-v8 npm install -D @vitest/coverage-istanbul
|
維測試介面
1
| npm install -D @vitest/ui
|
模擬 HTTP 請求
組件有串接Api時
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| GuessAge.vue <template> <h1 data-test="title">{{ title }}</h1> <div class="card"> <div style="width:400px;height:130px;margin-top:20px;border-style: dotted;" > <br> <span>Firsthand: {{firstname}}</span> <br> <span>Age: {{age}}</span>
</div> <label> Enter Firstname </label><br> <input type="text" v-model="search" style="font-size:20px;border-radius:10px; border:2px solid red"/> <a style="width:50px;height: 50px; background:red;" data-test="getAge" @click="getAge">Guess Age</a> <input type="radio" value="pop"> <label>Save my data</label> </div> </template>
<script setup lang="ts"> import { ref,computed } from 'vue'
const props = defineProps({ title:{type:String}, }) const search = ref<string>(''); const age = ref<string>(''); const firstname = ref<string>(''); const getAge =() => { fetch('https://api.agify.io/?name='+ search.value) .then(response => response.json()) .then(data => { age.value = data.age firstname.value = data.name search.value="" }) } </script>
|
安裝 msw
1
| npm install msw --save-dev
|
建立假資料
新增src/mocks/handlers.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| // src/mocks/handlers.js //模擬 HTTP 請求 import { http, HttpResponse } from 'msw' const api = "https://api.agify.io/"; export const restHandlers = [ http.get(api, (req, res, ctx) => { const query = { age: 55, name: "tope" } console.log('ctx.json',ctx.json([ query])) return res(ctx.status(200), ctx.json([ query ])) }), ]
|
攔截 http 請求
生成地方
1 2 3 4
| // npx msw init 生成模擬Api資料夾 --save //這時候就會在 public 資料夾下建立一個 mockServiceWorker.js 檔案,裏面就有攔截 http request 的程式碼了 npx msw init ./public --save
|
生成模擬Api資料夾
參考資料
GuessAge測試文件
GuessAge測試文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| //GuessAge.spec.js import { describe, it, expect, afterEach, beforeAll, afterAll, beforeEach } from 'vitest' import { setupServer } from 'msw/node' import { mount } from "@vue/test-utils"; import GuessAge from "../GuessAge.vue"; //引入Api模擬 import { restHandlers } from "../../mocks/handlers";
const server = setupServer(...restHandlers) // 在所有測試之前啟動伺服器 beforeAll(() => server.listen({ onUnhandledRequest: 'error' })) // 所有測試後關閉伺服器 afterAll(() => server.close()) // 每次測試後重置處理程序“對於測試隔離很重要” afterEach(() => server.resetHandlers()) describe('GuessAge', () => { let wrapper const title = 'Guess User Age App' if (typeof document !== 'undefined') { beforeEach(() => { wrapper = mount(GuessAge, { props: { title } }) }) } it("測試 GuessAge 元件 props title", async () => { if (typeof document !== 'undefined') { expect(wrapper.find('[data-test="title"]').text()).toBe(title); } }); it("測試資料是否為函數", () => { expect(typeof GuessAge.data).toBe("undefined"); }); it('快照 UI 測試', () => { if (typeof document !== 'undefined') { const wrapper = mount(GuessAge, {}); expect(wrapper.text()).toMatchSnapshot() } }) it("找到按鈕", () => { if (typeof document !== 'undefined') { expect(wrapper.find('[data-test="getAge"]').exists()).toBe(true); } });
it("按鈕點擊", async () => { if (typeof document !== 'undefined') { const ac = await wrapper.get('[data-test="getAge"]').trigger("click") expect(wrapper.vm.search).toEqual("") } }) })
|
在元件測試中模擬 DOM API
安裝happy-dom
1
| npm install happy-dom@6.0.4
|
1
| npm i -D @vitest/coverage-istanbul
|
目錄
參考資料