來更新一下 <NestedRoute>:
<template> <div> Nested Route <div class="username"> {{ $route.params.username }} </div> </div> </template>
現(xiàn)在報錯變?yōu)榱耍?/p>
tests/unit/NestedRoute.spec.js
NestedRoute
✕ renders a username from query string (17ms)
● NestedRoute › renders a username from query string
TypeError: Cannot read property 'params' of undefined
這是因為 $route 并不存在。 我們當然可以用一個真正的路由,但在這樣的情況下只用一個 mocks 加載選項會更容易些:
it("renders a username from query string", () => { const username = "alice" const wrapper = shallowMount(NestedRoute, { mocks: { $route: { params: { username } } } }) expect(wrapper.find(".username").text()).toBe(username) })
這樣測試就能通過了。在本例中,我們沒有做任何的導(dǎo)航或是和路由的實現(xiàn)相關(guān)的任何其他東西,所以 mocks 就挺好。我們并不真的關(guān)心 username 是從查詢字符串中怎么來的,只要它出現(xiàn)就好。
測試路由鉤子的策略
Vue Router 提供了多種類型的路由鉤子, 稱為 “navigation guards”。舉兩個例子如:
要確保這些運作正常,一般是集成測試的工作,因為需要一個使用者從一個理由導(dǎo)航到另一個。但也可以用單元測試檢驗導(dǎo)航 guards 中調(diào)用的函數(shù)是否正常工作,并更快的獲得潛在錯誤的反饋。這里列出一些如何從導(dǎo)航 guards 中解耦邏輯的策略,以及為此編寫的單元測試。
全局 guards
比方說當路由中包含 shouldBustCache 元數(shù)據(jù)的情況下,有那么一個 bustCache 函數(shù)就應(yīng)該被調(diào)用。路由可能長這樣:
//routes.js import NestedRoute from "@/components/NestedRoute.vue" export default [ { path: "/nested-route", component: NestedRoute, meta: { shouldBustCache: true } } ]
之所以使用 shouldBustCache 元數(shù)據(jù),是為了讓緩存無效,從而確保用戶不會取得舊數(shù)據(jù)。一種可能的實現(xiàn)如下:
//router.js import Vue from "vue" import VueRouter from "vue-router" import routes from "./routes.js" import { bustCache } from "./bust-cache.js" Vue.use(VueRouter) const router = new VueRouter({ routes }) router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.shouldBustCache)) { bustCache() } next() }) export default router
在單元測試中,你可能想導(dǎo)入 router 實例,并試圖通過 router.beforeHooks[0]() 的寫法調(diào)用 beforeEach;但這將拋出一個關(guān)于 next 的錯誤 -- 因為沒法傳入正確的參數(shù)。針對這個問題,一種策略是在將 beforeEach 導(dǎo)航鉤子耦合到路由中之前,解耦并單獨導(dǎo)出它。做法是這樣的:
//router.js export function beforeEach((to, from, next) { if (to.matched.some(record => record.meta.shouldBustCache)) { bustCache() } next() } router.beforeEach((to, from, next) => beforeEach(to, from, next)) export default router
再寫測試就容易了,雖然寫起來有點長:
import { beforeEach } from "@/router.js" import mockModule from "@/bust-cache.js" jest.mock("@/bust-cache.js", () => ({ bustCache: jest.fn() })) describe("beforeEach", () => { afterEach(() => { mockModule.bustCache.mockClear() }) it("busts the cache when going to /user", () => { const to = { matched: [{ meta: { shouldBustCache: true } }] } const next = jest.fn() beforeEach(to, undefined, next) expect(mockModule.bustCache).toHaveBeenCalled() expect(next).toHaveBeenCalled() }) it("busts the cache when going to /user", () => { const to = { matched: [{ meta: { shouldBustCache: false } }] } const next = jest.fn() beforeEach(to, undefined, next) expect(mockModule.bustCache).not.toHaveBeenCalled() expect(next).toHaveBeenCalled() }) })
最主要的有趣之處在于,我們借助 jest.mock,mock 掉了整個模塊,并用 afterEach 鉤子將其復(fù)原。通過將 beforeEach 導(dǎo)出為一個已結(jié)耦的、普通的 Javascript 函數(shù),從而讓其在測試中不成問題。
為了確定 hook 真的調(diào)用了 bustCache 并且顯示了最新的數(shù)據(jù),可以使用一個諸如 Cypress.io 的端到端測試工具,它也在應(yīng)用腳手架 vue-cli 的選項中提供了。
組件 guards
一旦將組件 guards 視為已結(jié)耦的、普通的 Javascript 函數(shù),則它們也是易于測試的。假設(shè)我們?yōu)?<NestedRoute> 添加了一個 beforeRouteLeave hook:
//NestedRoute.vue <script> import { bustCache } from "@/bust-cache.js" export default { name: "NestedRoute", beforeRouteLeave(to, from, next) { bustCache() next() } } </script>
對在全局 guard 中的方法照貓畫虎就可以測試它了:
// ... import NestedRoute from "@/compoents/NestedRoute.vue" import mockModule from "@/bust-cache.js" jest.mock("@/bust-cache.js", () => ({ bustCache: jest.fn() })) it("calls bustCache and next when leaving the route", () => { const next = jest.fn() NestedRoute.beforeRouteLeave(undefined, undefined, next) expect(mockModule.bustCache).toHaveBeenCalled() expect(next).toHaveBeenCalled() })
這樣的單元測試行之有效,可以在開發(fā)過程中立即得到反饋;但由于路由和導(dǎo)航 hooks 常與各種組件互相影響以達到某些效果,也應(yīng)該做一些集成測試以確保所有事情如預(yù)期般工作。
總結(jié)
本文講述了:
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com