欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

對 Vue-Router 進行單元測試的方法

 更新時間:2018年11月05日 13:44:31   作者:lua  
這篇文章主要介紹了對 Vue-Router 進行單元測試的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

由于路由通常會把多個組件牽扯到一起操作,所以一般對其的測試都在 端到端/集成 階段進行,處于測試金字塔的上層。不過,做一些路由的單元測試還是大有益處的。

對于與路由交互的組件,有兩種測試方式:

  • 使用一個真正的 router 實例
  • mock 掉 $route  和 $router  全局對象

因為大多數(shù) Vue 應用用的都是官方的 Vue Router,所以本文會談談這個。

創(chuàng)建組件

我們會弄一個簡單的 <App>,包含一個  /nested-child  路由。訪問 /nested-child  則渲染一個 <NestedRoute>  組件。創(chuàng)建 App.vue  文件,并定義如下的最小化組件:

<template>
 <div id="app">
  <router-view />
 </div>
</template>

<script>
export default {
 name: 'app'
}
</script>

<NestedRoute>  同樣迷你:

<template>
 <div>Nested Route</div>
</template>

<script>
export default {
 name: "NestedRoute"
}
</script>

現(xiàn)在定義一個路由:

import NestedRoute from "@/components/NestedRoute.vue"

export default [
 { path: "/nested-route", component: NestedRoute }
]

在真實的應用中,一般會創(chuàng)建一個 router.js  文件并導入定義好的路由,寫出來一般是這樣的:

import Vue from "vue"
import VueRouter from "vue-router"
import routes from "./routes.js"

Vue.use(VueRouter)

export default new VueRouter({ routes })

為避免調用 Vue.use(...)  污染測試的全局命名空間,我們將會在測試中創(chuàng)建基礎的路由;這讓我們能在單元測試期間更細粒度的控制應用的狀態(tài)。

編寫測試

先看點代碼再說吧。我們來測試 App.vue,所以相應的增加一個  App.spec.js:

import { shallowMount, mount, createLocalVue } from "@vue/test-utils"
import App from "@/App.vue"
import VueRouter from "vue-router"
import NestedRoute from "@/components/NestedRoute.vue"
import routes from "@/routes.js"

const localVue = createLocalVue()
localVue.use(VueRouter)

describe("App", () => {
 it("renders a child component via routing", () => {
  const router = new VueRouter({ routes })
  const wrapper = mount(App, { localVue, router })

  router.push("/nested-route")

  expect(wrapper.find(NestedRoute).exists()).toBe(true)
 })
})

照例,一開始先把各種模塊引入我們的測試;尤其是引入了應用中所需的真實路由。這在某種程度上很理想 -- 若真實路由一旦掛了,單元測試就失敗,這樣我們就能在部署應用之前修復這類問題。

可以在 <App>  測試中使用一個相同的 localVue,并將其聲明在第一個  describe  塊之外。而由于要為不同的路由做不同的測試,所以把 router  定義在 it  塊里。

另一個要注意的是這里用了 mount  而非 shallowMount。如果用了  shallowMount,則  <router-link>  就會被忽略,不管當前路由是什么,渲染的其實都是一個無用的替身組件。

為使用了 mount 的大型渲染樹做些變通

使用 mount  在某些情況下很好,但有時卻是不理想的。比如,當渲染整個 <App>  組件時,正趕上渲染樹很大,包含了許多組件,一層層的組件又有自己的子組件。這么些個子組件都要觸發(fā)各種生命周期鉤子、發(fā)起 API 請求什么的。

如果你在用 Jest,其強大的 mock 系統(tǒng)為此提供了一個優(yōu)雅的解決方法??梢院唵蔚?mock 掉子組件,在本例中也就是 <NestedRoute>。使用了下面的寫法后,以上測試也將能通過:

jest.mock("@/components/NestedRoute.vue", () => ({
 name: "NestedRoute",
 render: h => h("div")
}))

使用 Mock Router

有時真實路由也不是必要的?,F(xiàn)在升級一下 <NestedRoute>,讓其根據(jù)當前 URL 的查詢字符串顯示一個用戶名。這次我們用 TDD 實現(xiàn)這個特性。以下是一個基礎測試,簡單的渲染了組件并寫了一句斷言:

import { shallowMount } from "@vue/test-utils"
import NestedRoute from "@/components/NestedRoute.vue"
import routes from "@/routes.js"

describe("NestedRoute", () => {
 it("renders a username from query string", () => {
  const username = "alice"
  const wrapper = shallowMount(NestedRoute)

  expect(wrapper.find(".username").text()).toBe(username)
 })
})

然而我們并沒有 <div class="username">  ,所以一運行測試就會報錯:

tests/unit/NestedRoute.spec.js
  NestedRoute
    ✕ renders a username from query string (25ms)

  ● NestedRoute › renders a username from query string

    [vue-test-utils]: find did not return .username, cannot call text() on empty Wrapper

來更新一下 <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)
})

這樣測試就能通過了。在本例中,我們沒有做任何的導航或是和路由的實現(xiàn)相關的任何其他東西,所以 mocks  就挺好。我們并不真的關心 username  是從查詢字符串中怎么來的,只要它出現(xiàn)就好。

測試路由鉤子的策略

Vue Router 提供了多種類型的路由鉤子, 稱為 “navigation guards”。舉兩個例子如:

  • 全局 guards (router.beforeEach)。在 router 實例上聲明
  • 組件內 guards,比如 beforeRouteEnter。在組件中聲明

要確保這些運作正常,一般是集成測試的工作,因為需要一個使用者從一個理由導航到另一個。但也可以用單元測試檢驗導航 guards 中調用的函數(shù)是否正常工作,并更快的獲得潛在錯誤的反饋。這里列出一些如何從導航 guards 中解耦邏輯的策略,以及為此編寫的單元測試。

全局 guards

比方說當路由中包含 shouldBustCache  元數(shù)據(jù)的情況下,有那么一個 bustCache  函數(shù)就應該被調用。路由可能長這樣:

//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

在單元測試中,你可能想導入 router 實例,并試圖通過 router.beforeHooks[0]()  的寫法調用 beforeEach;但這將拋出一個關于  next  的錯誤 -- 因為沒法傳入正確的參數(shù)。針對這個問題,一種策略是在將 beforeEach  導航鉤子耦合到路由中之前,解耦并單獨導出它。做法是這樣的:

//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  鉤子將其復原。通過將 beforeEach  導出為一個已結耦的、普通的 Javascript 函數(shù),從而讓其在測試中不成問題。

為了確定 hook 真的調用了 bustCache  并且顯示了最新的數(shù)據(jù),可以使用一個諸如 Cypress.io 的端到端測試工具,它也在應用腳手架 vue-cli  的選項中提供了。

組件 guards

一旦將組件 guards 視為已結耦的、普通的 Javascript 函數(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ā)過程中立即得到反饋;但由于路由和導航 hooks 常與各種組件互相影響以達到某些效果,也應該做一些集成測試以確保所有事情如預期般工作。

總結

本文講述了:

  • 測試由 Vue Router 條件渲染的組件
  • 用 jest.mock  和 localVue  去 mock Vue 組件
  • 從 router 中解耦全局導航 guard 并對其獨立測試
  • 用 jest.mock  來 mock 一個模塊

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • 解決Vue運行項目報錯proxy?error:?could?not?proxy?request

    解決Vue運行項目報錯proxy?error:?could?not?proxy?request

    這篇文章主要給大家介紹了關于如何解決Vue運行項目報錯proxy?error:could?not?proxy?request的相關資料,Proxy Error指的是代理服務器無法正確處理請求的錯誤,需要的朋友可以參考下
    2023-08-08
  • vue 組件的封裝之基于axios的ajax請求方法

    vue 組件的封裝之基于axios的ajax請求方法

    今天小編就為大家分享一篇vue 組件的封裝之基于axios的ajax請求方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • vue2封裝webSocket的實現(xiàn)(開箱即用)

    vue2封裝webSocket的實現(xiàn)(開箱即用)

    在Vue2中,可以使用WebSocket實時通信,本文主要介紹了vue2封裝webSocket的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-08-08
  • vue?ui的安裝步驟以及使用詳解

    vue?ui的安裝步驟以及使用詳解

    最近公司開發(fā)一個項目,采用的前后端分離的方式,前端采用vue,但是再項目開發(fā)過程中遇到一個比較麻煩的問題,下面這篇文章主要給大家介紹了關于vue?ui的安裝步驟以及使用的相關資料,需要的朋友可以參考下
    2022-08-08
  • vue.config.js中的devServer使用

    vue.config.js中的devServer使用

    這篇文章主要介紹了vue.config.js中的devServer使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • 基于Vue3實現(xiàn)掃碼槍掃碼并生成二維碼實例代碼

    基于Vue3實現(xiàn)掃碼槍掃碼并生成二維碼實例代碼

    vue3生成二維碼的方式有很多種,下面這篇文章主要給大家介紹了關于如何基于Vue3實現(xiàn)掃碼槍掃碼并生成二維碼的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-06-06
  • vue3使用vue-router嵌套多級路由的方法

    vue3使用vue-router嵌套多級路由的方法

    Vue3 嵌套路由的使用和 Vue2 相差不大,主要的區(qū)別是 Vue3 的路由實例化使用了 createApp() 方法,所以實例化路由時需要傳入根組件,這篇文章主要介紹了vue3使用vue-router嵌套路由(多級路由),需要的朋友可以參考下
    2023-12-12
  • elementUi 中table表尾插入行的實例

    elementUi 中table表尾插入行的實例

    這篇文章主要介紹了elementUi 中table表尾插入行的實例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • Vue?列表過濾與排序的實現(xiàn)

    Vue?列表過濾與排序的實現(xiàn)

    這篇文章主要介紹了Vue?列表過濾與排序的實現(xiàn),文章圍繞主題展開詳細的內容介紹,具有一定的參考價值需要的小伙伴可以參考一下
    2022-05-05
  • vue-cli 引入jQuery,Bootstrap,popper的方法

    vue-cli 引入jQuery,Bootstrap,popper的方法

    這篇文章主要介紹了vue-cli 引入jQuery,Bootstrap,popper的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09

最新評論