您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Vue組件單元測試的示例分析,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
關于 Vue 組件單元測試最常見的問題就是“我究竟應該測試什么?”
雖然測試過多或過少都是可能的,但我的觀察是,開發人員通常會測試過頭。畢竟,沒有人愿意自己的組件未經測試從而導致應用程序在生產中崩潰。
在本文中,我將分享一些用于組件單元測試的指導原則,這些指導原則可以確保在編寫測試上不會花費大量時間,但是可以提供足夠的覆蓋率來避免錯誤。
本文假設你已經了解 Jest 和 Vue Test Utils。
示例組件
在學習這些指導原則之前,我們先來熟悉下要測試的示例組件。組件名為 Item.vue ,是 eCommerce App 里的一個產品條目。
下面是組件的源碼。注意有三個依賴項:Vuex ( $store
), Vue Router ( $router
) 和 Vue Auth ( $auth
)。
Item.vue
<template> <div> <h3>{{ item.title }}</h3> <button @click="addToCart">Add To Cart</button> <img :src="item.image"/> </div> </template> <script> export default { name: "Item", props: [ "id" ], computed: { item () { return this.$store.state.find( item => item.id === this.id ); } }, methods: { addToCart () { if (this.$auth.check()) { this.$store.commit("ADD_TO_CART", this.id); } else { this.$router.push({ name: "login" }); } } } }; </script>
配置 Spec 文件
下面是測試用的 spec 文件。其中,我們將用 Vue Test Utils “淺掛載”示例組件,因此引入了相關模塊以及我們要測試的 Item 組件。
同時還寫了一個工廠函數用于生成可覆蓋的配置對象,以免在每個測試中都需要指定 props 和 mock 三個依賴項。 item.spec.js
import { shallowMount } from "@vue/test-utils"; import Item from "@/components/Item"; function createConfig (overrides) { const id = 1; const mocks = { // Vue Auth $auth: { check: () => false }, // Vue Router $router: { push: () => {} }, // Vuex $store: { state: [ { id } ], commit: () => {} } }; const propsData = { id }; return Object.assign({ mocks, propsData }, overrides); } describe("Item.vue", () => { // Tests go here });
確定業務邏輯
對于要測試的組件,要問的第一個也是最重要的問題是“業務邏輯是什么”,即組件是做什么的?
對于這個 Item.vue ,業務邏輯是:
根據接收的id屬性展示條目信息
如果用戶是訪客,點擊 Add to Cart 按鈕將重定向到登錄頁
如果用戶已登錄,點擊 Add to Cart 按鈕會觸發 Vuex mutation ADD_TO_CART。
確定輸入和輸出
當你對組件做單元測試時,可將其視為一個黑盒。方法、計算屬性等內部邏輯只影響輸出。
因此,下一個重點是確定組件的輸入和輸出,因為這些也是測試的輸入和輸出。
Item.vue 的輸入是:
id 屬性
來自 Vuex 和 Vue Auth 的數據狀態
用戶點擊按鈕
輸出是:
渲染后的 HTML
發送到 Vuex mutation 或者 Vue Router push 的數據
有些組件也會將表單和事件作為輸入,觸發事件作為輸出。
測試 1: 訪客點擊按鈕跳轉路由
有一個業務邏輯是“如果用戶是訪客,點擊 Add to Cart 按鈕將重定向到登錄頁”。我們來寫這個測試。
我們通過“shallow mount”組件來編寫測試,然后找到并點擊 Add to Cart 按鈕。
test("router called when guest clicks button", () => { const config = createConfig(); const wrapper = shallowMount(Item, config); wrapper .find("button") .trigger("click"); // Assertion goes here }
隨后我們會加上 assertion。
不要超出輸入和輸出的界限
在這個測試中很容易采取的做法是在點擊按鈕后判斷路由是否跳轉到了登錄頁,比如:
import router from "router"; test("router called when guest clicks button", () => { ... // 錯! const route = router.find(route => route.name === "login"); expect(wrapper.vm.$route.path).toBe(route.path); }
雖然這確實也能測試組件的輸出,但是它依賴于路由功能,這不應該是組件所關心的。
直接測試組件的輸出會更好,也就是調用了 $router.push
。至于路由是否最終完成了操作,這已經超出了本測試的范疇。
因此我們可以監聽路由的 push
方法,并斷言它是否被登錄路由對象調用。
import router from "router"; test("router called when guest clicks button", () => { ... jest.spyOn(config.mocks.$router, "push"); const route = router.find(route => route.name === "login"); expect(spy).toHaveBeenCalledWith(route); }
測試 2: 登錄用戶點擊按鈕后調用 vuex
接下來讓我們測試業務邏輯“如果用戶已登錄,點擊 Add to Cart 按鈕將觸發 Vuex mutation ADD_TO_CART
”。
同樣,你不需要判斷 Vuex 狀態是否更改了。要驗證這個需要另外單獨測試 Vuex store。
組件的職責只是執行 commit,因此我們只要測試這個動作就行。
首先重寫 $auth.check
假數據讓它返回 true
(模擬登錄用戶)。然后監聽 store 的 commit
方法,并斷言點擊按鈕后被調用。
test("vuex called when auth user clicks button", () => { const config = createConfig({ mocks: { $auth: { check: () => true } } }); const spy = jest.spyOn(config.mocks.$store, "commit"); const wrapper = shallowMount(Item, config); wrapper .find("button") .trigger("click"); expect(spy).toHaveBeenCalled(); }
不要測試其他庫的功能
Item 組件展示條目數據,特別是標題和圖片。或許我們應該寫一個測試來專門檢查這些?比如:
test("renders correctly", () => { const wrapper = shallowMount(Item, createConfig()); // Wrong expect(wrapper.find("h3").text()).toBe(item.title); }
這又是一個不必要的測試,因為它只是測試了 Vue 從 Vuex 中提取數據并插入到模板的能力。Vue 這個庫已經對該機制進行了測試,所以你應該依賴于它。
測試 3: 正確地渲染
但是等等,如果有人不小心將 title
重命名為 name
,然后忘記更新插值表達式怎么辦?這難道不需要測試嗎?
沒錯,但是如果你像這樣來測試模板的方方面面,何時才是個頭?
測試 HTML 最好的辦法是使用快照,用來檢查整體渲染后的結果。這不僅覆蓋了標題插值,還包括圖片、按鈕文本、任何 class 等。
test("renders correctly", () => { const wrapper = shallowMount(Item, createConfig()); expect(wrapper).toMatchSnapshot(); });
其他不需要測試的點還有這些:
src 屬性是否綁定到 img 元素
添加到 Vuex store 中的數據是否跟插入的數據一致
計算屬性是否返回了正確的數據
執行 router push 是否重定向到正確的頁面
關于“Vue組件單元測試的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。