如果有任何問題或建議,歡迎隨時聯繫我:
嘿,各位 Vue 的品質鑑定師!昨天我們扮演了一位「精密儀器技師」,拿著 @vue/test-utils
這個高科技放大鏡,仔細檢查元件的內部構造。
class
清單裡,確實有 is-disabled
!」click
事件確實有發射成功!」這些測試非常精準,就像一份詳細的「零件出廠品管報告」。它證明了我們做的每個零件都符合規格。但...它能保證組裝起來的「成品」好用嗎?
試想一下:
一個拿到新手機的使用者,他才不管螢幕底下那顆按鈕的 class 叫什麼,他只在乎:「為什麼這個灰色的按鈕我按不下去?」
一個想登入網站的使用者,他根本不知道什麼是
emit
,他只關心:「我點了登入,然後呢?怎麼沒反應?」
看到了嗎?「零件合格」跟「使用者覺得好用」之間,還有一段距離。
今天,我們要進行一個角色大轉變!我們要從「技師」變身為「真正的使用者」,甚至是一位挑剔的「秘密客」。我們要介紹的這位新夥伴——Testing Library,就是為此而生的。
它的核心精神,就像一句廣告詞:
「我們不只關心產品怎麼做出來的,我們更關心你用起來爽不爽!」
如果說昨天的測試是在「實驗室」裡做檢測,那今天的測試,就是把產品直接丟到「真實市場」去,讓使用者來一場貨真價實的「大家來找碴」!
Testing Library 最酷的地方,就是它會故意「蒙上你的雙眼」,讓你沒辦法輕易看到元件的內部零件(像是 data
、props
、class
名稱)。它強迫你戴上「使用者的眼鏡」,從他的視角來跟你的應用程式互動。
它鼓勵你這麼做:
像使用者一樣「找東西」:你不是用 id
或 class
這種開發者才知道的暗號去找按鈕。你得像個真人一樣,用眼睛去找「畫面上寫著『提交』的按鈕」、找「標籤是『使用者名稱』的輸入框」。
像使用者一樣「動手玩」:你不是冷冰冰地 trigger
一個點擊事件。你要模擬一個完整的人類行為,像是用滑鼠「點擊」那個按鈕,或是在輸入框裡「打字」。
像使用者一樣「看結果」:你不斷言某個變數變成 true
。你斷言的是:「我點了登入後,畫面上是不是出現了『歡迎回來!』這句話?」
這種轉變,就像是從「檢查食譜的每個步驟是否正確」變成「親口嚐嚐這道菜好不好吃」。
這樣寫出來的測試,會變得超級健壯!就算你今天心血來潮,把廚房(元件內部)整個大改造,換了鍋子、改了爐子,只要最後端出來的「宮保雞丁」味道還是對的,那「美食家」(測試)就會給你一個讚,測試照樣通過!這才是我們追求的終極目標:確保軟體真的能用!
要開始我們的「秘密客任務」,需要先招募幾位新夥伴:
@testing-library/vue
: 我們的核心工具,Vue 專屬的「使用者眼鏡」。@testing-library/user-event
: 一位超強的「行為模仿大師」,能更逼真地模擬使用者點擊、打字等動作。@testing-library/jest-dom
: 一本「斷言字典」,提供更多符合直覺的判斷詞,例如 在不在畫面上()
。用下面這行咒語把他們召喚過來:
npm install -D @testing-library/vue @testing-library/user-event @testing-library/jest-dom
為了讓我們的測試環境 Vitest 看得懂這本新字典,我們需要做個小小的設定。
建立 vitest.setup.js
於專案根目錄(如果還沒有的話):
// vitest.setup.js
// 告訴 Vitest:「嘿,記得帶上這本新字典喔!」
import { expect } from 'vitest';
import '@testing-library/jest-dom/vitest';
修改 vite.config.js
,讓 Vitest 知道要去哪裡找這個設定檔:
// vite.config.js
/// <reference types="vitest" />
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
test: {
globals: true,
environment: 'jsdom',
// 在這裡告訴 Vitest 我們的設定檔在哪
setupFiles: ['./vitest.setup.js'],
},
});
光說不練假把戲!讓我們用 Testing Library 來測試一個簡單的計數器元件。
我們的元件 (Counter.vue
)
// src/components/Counter.vue
<template>
<div>
<p>當前計數:{{ count }}</p>
<button @click="increment">增加</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const count = ref(0);
const increment = () => count.value++;
</script>
我們的「使用者視角」測試 (Counter.spec.js
)
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import Counter from './Counter.vue';
describe('Counter.vue', () => {
it('should display initial count and increment when button is clicked', async () => {
// 1. 準備:把元件渲染到畫面上
render(Counter);
// 2. 找碴:像使用者一樣,用「看到的文字」去找元素
// 找看看畫面上是不是有「當前計數:0」
expect(screen.getByText('當前計數:0')).toBeInTheDocument();
// 接著,找看看有沒有一個叫「增加」的按鈕
const incrementButton = screen.getByRole('button', { name: '增加' });
// 3. 互動:像使用者一樣,用滑鼠去「點」那個按鈕
await userEvent.click(incrementButton);
// 4. 驗收:看看結果是不是如預期
// 畫面上的計數,是不是變成「當前計數:1」了?
expect(screen.getByText('當前計數:1')).toBeInTheDocument();
// 舊的計數是不是不見了?
expect(screen.queryByText('當前計數:0')).not.toBeInTheDocument();
});
});
看到了嗎?整個測試流程沒有一個 wrapper.find
,沒有一個 .trigger
,更沒有檢查 class
或 id
。整個過程就像在描述一位使用者如何與你的網頁互動,非常直觀!
LoginForm.vue
元件:包含 email、password 兩個輸入框和一個「登入」按鈕。@testing-library/vue
和 user-event
。今天我們進行了一次重要的「思維轉換」。我們從關心「元件內部實現」的技師,轉變為關心「使用者真實體驗」的秘密客。
screen.getByRole
、screen.getByText
等查詢方式,像真人一樣在畫面上找東西。userEvent
來模擬真實的使用者互動,例如點擊和打字。掌握了 Testing Library,你就掌握了撰寫高價值、高穩定性前端測試的關鍵。你的測試不再是易碎的玻璃,而是堅固的護城河。
明天,我們將探討一個在真實世界中不可避免的議題:如何測試那些需要呼叫 API 的非同步元件。
Testing Library
使用者中心測試 (User-centric Testing)
getByRole
, getByText
user-event
行為測試 (Behavioral Testing)
實作細節 (Implementation Details)
健壯性 (Resilience)