最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501
当前位置: 首页 - 科技 - 知识百科 - 正文

对 Vue-Router 进行单元测试的方法

来源:懂视网 责编:小采 时间:2020-11-27 22:04:29
文档

对 Vue-Router 进行单元测试的方法

对 Vue-Router 进行单元测试的方法:由于路由通常会把多个组件牵扯到一起操作,所以一般对其的测试都在 端到端/集成 阶段进行,处于测试金字塔的上层。不过,做一些路由的单元测试还是大有益处的。 对于与路由交互的组件,有两种测试方式: 使用一个真正的 router 实例 mock 掉 $route
推荐度:
导读对 Vue-Router 进行单元测试的方法:由于路由通常会把多个组件牵扯到一起操作,所以一般对其的测试都在 端到端/集成 阶段进行,处于测试金字塔的上层。不过,做一些路由的单元测试还是大有益处的。 对于与路由交互的组件,有两种测试方式: 使用一个真正的 router 实例 mock 掉 $route

来更新一下 <NestedRoute>:

<template>
 <div>
 Nested Route
 <div class="username">
 {{ $route.params.username }}
 </div>
 </div>
</template>

现在报错变为了:

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)
})

这样测试就能通过了。在本例中,我们没有做任何的导航或是和路由的实现相关的任何其他东西,所以 mocks  就挺好。我们并不真的关心 username  是从查询字符串中怎么来的,只要它出现就好。

测试路由钩子的策略

Vue Router 提供了多种类型的路由钩子, 称为 “navigation guards”。举两个例子如:

  • 全局 guards (router.beforeEach)。在 router 实例上声明
  • 组件内 guards,比如 beforeRouteEnter。在组件中声明
  • 要确保这些运作正常,一般是集成测试的工作,因为需要一个使用者从一个理由导航到另一个。但也可以用单元测试检验导航 guards 中调用的函数是否正常工作,并更快的获得潜在错误的反馈。这里列出一些如何从导航 guards 中解耦逻辑的策略,以及为此编写的单元测试。

    全局 guards

    比方说当路由中包含 shouldBustCache  元数据的情况下,有那么一个 bustCache  函数就应该被调用。路由可能长这样:

    //routes.js
    
    import NestedRoute from "@/components/NestedRoute.vue"
    
    export default [
     {
     path: "/nested-route",
     component: NestedRoute,
     meta: {
     shouldBustCache: true
     }
     }
    ]
    
    

    之所以使用 shouldBustCache  元数据,是为了让缓存无效,从而确保用户不会取得旧数据。一种可能的实现如下:

    //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  的错误 -- 因为没法传入正确的参数。针对这个问题,一种策略是在将 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 函数,从而让其在测试中不成问题。

    为了确定 hook 真的调用了 bustCache  并且显示了最新的数据,可以使用一个诸如 Cypress.io 的端到端测试工具,它也在应用脚手架 vue-cli  的选项中提供了。

    组件 guards

    一旦将组件 guards 视为已结耦的、普通的 Javascript 函数,则它们也是易于测试的。假设我们为 <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()
    })
    
    

    这样的单元测试行之有效,可以在开发过程中立即得到反馈;但由于路由和导航 hooks 常与各种组件互相影响以达到某些效果,也应该做一些集成测试以确保所有事情如预期般工作。

    总结

    本文讲述了:

  • 测试由 Vue Router 条件渲染的组件
  • 用 jest.mock  和 localVue  去 mock Vue 组件
  • 从 router 中解耦全局导航 guard 并对其独立测试
  • 用 jest.mock  来 mock 一个模块
  • 声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文档

    对 Vue-Router 进行单元测试的方法

    对 Vue-Router 进行单元测试的方法:由于路由通常会把多个组件牵扯到一起操作,所以一般对其的测试都在 端到端/集成 阶段进行,处于测试金字塔的上层。不过,做一些路由的单元测试还是大有益处的。 对于与路由交互的组件,有两种测试方式: 使用一个真正的 router 实例 mock 掉 $route
    推荐度:
    标签: VUE 的方法 对于
    • 热门焦点

    最新推荐

    猜你喜欢

    热门推荐

    专题
    Top