当前位置: 首页 > 工具软件 > Vitest > 使用案例 >

vitest 单元测试配合@vue/test-utils 之 router 篇

司徒俊良
2023-12-01

what is vitest & VueTestUtils & VueRouter

vitest 是由 vite 提供支持的极速单元测试框架,VueTestUtils 是 Vue.js 的官方测试实用程序库,Vue Router 是 Vue.js 的官方路由,以上均为各自官网对其的描述

demo

项目中使用路由是十分常见,所以对它来个单元测试也是十分必要的

// app.vue
<template>
  <nav>
    <RouterLink to="/">home</RouterLink>
    <RouterLink data-test="about" to="/about">about</RouterLink>
  </nav>
  <RouterView />
</template>

// home.vue
<template>
  <div>主页</div>
</template>

// about.vue
<template>
  <div>关于</div>
</template>
// spec | test
import { describe, it, expect } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { createRouter, createWebHistory } from 'vue-router'
import App from '@/App.vue'
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
describe('router-test', () => {
  const router = createRouter({
    history: createWebHistory(),
    routes: [
      { path: '/', component: Home },
      { path: '/about', component: About },
    ],
  })
  it('test1', async () => {
    router.push('/')
    await router.isReady()
    const wrapper = mount(App, {
      global: { plugins: [router] },
    })
    expect(wrapper.findComponent(Home).exists()).toBe(true)
  })
  it('test2', async () => {
    router.push('/')
    await router.isReady()
    const wrapper = mount(App, {
      global: { plugins: [router] },
    })
    expect(wrapper.html()).toContain('主页')
    await wrapper.find('[data-test="about"]').trigger('click')
    await flushPromises()
    expect(wrapper.html()).toContain('关于')
  })
})
  1. 此 demo 由组件部分和测试文件两个部分组成
  2. app 组件里是一个基础的单页应用,点击对应的 route-link,route-view 中展示对应组件
  3. 测试文件因为是在单元测试环境下,所以我们需要先安装 VueRouter,VueRouter 是一个插件,在挂载组件时使用安装选项安装global: {plugins: []}

    第一组测试首先执行了跳转,因为 VueRouter4 异步处理路由,需要告诉路由器何时准备就绪,所以使用了await router.isReady()

    组件通过 test-utils 库的查找组件的方法findComponet查找 Home,再通过exists()验证元素是否存在
    然后使用 vitest 库的 expect 创建断言,toBe 断言基础对象是否相等

    第二组测试初始部分与第一组相同

    组件通过 test-utils 库 html 方法返回元素的 HTML
    然后使用 vitest 库的 expect 创建断言,toContain 断言检查值是否在数组中
    再通过 find 方法返回查找元素,trigger 方法触发 DOM 事件,模拟用户点击的操作,同样是因为 VueRouter 的异步性质,我们需要再断言之前完成路由,由于没有可以等待的hasNavigated钩子,所以用 test-utils 提供的flushPromises替代,该方法会刷新所有已解决的 promise 程序,点击前后都断言了,会判断实际执行结果与预期结果是否一致,如果不一致则会抛出错误

tips

同样的这是一个简单的 demo,但它还是有很多值得注意的知识点

  • exists 使用该方法验证 findComponent 查找的组件是否存在,并返回一个 Boolean 值
  • flushPromise 刷新所有已解决的 promise 程序
  • 实际开发中还会有使用编程式导航的场景,通常是页面有一个按钮,按钮的点击事件执行router.push('xx')这样子,针对它的单元测试可以这样写
it('编程式导航', async () => {
  const wrapper = mount(App, {
    global: { plugins: [router] },
  })
  // vi.spyOn 在对象的方法或 getter/setter 上创建一个监听
  const spy = vi.spyOn(router, 'push') // 监听router的push方法
  // 模拟用户按下按钮
  await wrapper.find('button').trigger('click')
  // toHaveBeenCalledTimes 断言检查一个函数是否被调用了一定的次数
  expect(spy).toHaveBeenCalledTimes(1)
  // toHaveBeenCalledWith 断言检查一个函数是否被调用过,并且传入了指定的参数
  expect(spy).toHaveBeenCalledWith('/about')
  // 后面就是和上面的测试代码一样的逻辑了
})

其他文章

vitest 单元测试配合@vue/test-utils 之组件单元测试篇
vitest 单元测试配合@vue/test-utils 之 pinia 篇

 类似资料: