vue单元测试vue test utils使用初探

栏目: 编程工具 · 发布时间: 5年前

内容简介:最近在做一个项目的重构,技术选型为vue-cli 3.0 + typescript + vue-router + sass.因为我负责的模块比较少比较简单,所以老大让我先把负责部分的测试代码写好。至此我才第一次接触到测试代码,我们项目使用的测试工具是jest,与vue官方出的单元测试工具库vue-test-utils配合使用。第一次接触测试代码,开始的时候还是一脸懵逼,有种学习一门新语言的赶脚。经过几天的摸索之后学会了简单的编写测试代码,并对几种情况进行特殊处理。本文是一篇vue单元测试的基础入门文章,只介

最近在做一个项目的重构,技术选型为vue-cli 3.0 + typescript + vue-router + sass.因为我负责的模块比较少比较简单,所以老大让我先把负责部分的测试代码写好。至此我才第一次接触到测试代码,我们项目使用的测试 工具 是jest,与vue官方出的单元测试工具库vue-test-utils配合使用。第一次接触测试代码,开始的时候还是一脸懵逼,有种学习一门新语言的赶脚。经过几天的摸索之后学会了简单的编写测试代码,并对几种情况进行特殊处理。本文是一篇vue单元测试的基础入门文章,只介绍测试代码,需要了解搭建测试框架的朋友可以自行参阅 vue-test-utils官方文档 等资料。

1.什么是测试代码

简言之,测试代码就是通过代码模拟一个vue组件的运行环境,使被测试的组件在这个环境下运行看是否能够得到期望的运行结果。如果运行结果与期望的结果相同,则说明该测试用例通过。下面我们来看一个最简单的实例:

  • 一个简单的vue组件的测试
// message.vue
<template>
  <div>
    <p>{{message}}</p>
  </div>
<template>
<script>
  export default{
    data(){
      return{
        message: 'a test component',
      }
    }
  }
</script>
复制代码

上面这个最简单的vue组件,就是将data中的message值渲染到p标签中去。对与这样的组件,测试代码如下

import { mount } from '@vue/test-utils';
import message from './message.vue';
describe('测试message.vue组件的测试套件,可含有多个测试用例', () =>{
  it('这是测试message组件p标签能否正常渲染文字的一个测试测试用例', () => {
    const wrapper = mount(message, {}) // 使用mount可以创建一个包涵被挂载和渲染的一个实例
    expect(wrapper.find('p').text()).toBe('a test component') // expect是jest中的断言,即判断该语句前后是否相等
  })
})
复制代码

上面这句expect().toBe()断言,用于判断我们用message.vue组件生成的实例中,p 标签中的文字是否等于组件 data 中的 message 的值'a test component',如果相等,则说明此断言为真。

  • 一个稍复杂组件的测试
// count.vue
<template>
  <div>
    <p>{{count}}</p>
    <button @click="add">增加</button>
  </div>
</template>
<script>
export default {
  data() {
    return{
      count: 0
    }
  },
  methods: {
    add() {
      this.count++
    }
  },
}
</script>
复制代码

上面是一个带有简单交互的vue组件,count初始值为0,用户点击一次增加按钮,p标签中的值即加一。对于这样的组件我们的测试代码为

import { mount } from '@vue/test-utils';
import count from 'count.vue';
describe('count.vue组件', () => {
  it('测试count组件能否正常显示并增加', () => {
    const wrapper = mount(count, {}) // 使用 mount 创建一个vue组件实例 wrapper
    expect(wrapper.find('p').text()).toBe(0); // 判断p标签中的值是否为初始化0
    wrapper.find('button').trigger('click'); // 使用trigger('click')模拟用户的点击操作
    expect(wrapper.find('p').text()).toBe(1); // 经过模拟点击操作后,count的值应该增加成为1
  })
})
复制代码

通过上面的例子我们看到,测试代码可以模拟出用户的操作,通过对操作之后的结果进行断言,能判断出该组件能否通过测试。而且一个测试用例中可以有多个断言,只有全部断言通过才说明该组件实例通过测试。只要有一个断言未通过,则说明该组件实例未通过测试。

2.测试带有回调的异步请求

在项目中我们很常见到vue组件内向接口请求数据的情况,那么我们如何测试这种异步请求呢。官方也给了我们实例代码:

// axios模拟
export default{
  get: () => Promise.resolve({ date: 'value' })
}
复制代码
// getValue.vue
<template>
  <button @click="fetchResults" />
</template>
<script>
  import axios from 'axios'
  export default {
    data() {
      return {
        value: null
      }
    },
    methods: {
      async fetchResults() {
        const response = await axios.get('mock/service')
        this.value = response.data
      }
    }
  }
</script>
复制代码
// getValue.test.js
import { shallowMount } from '@vue/test-utils'
import Foo from './Foo'
jest.mock('axios') // jest 模拟axios库

describe('getValue.vue组件', () => {
  it('点击button时,异步获取接口返回的value值', done => {
    const wrapper = shallowMount(Foo)
    wrapper.find('button').trigger('click')
    wrapper.vm.$nextTick(() => { // 使用$nextTick 在Promise执行后再进行断言
      expect(wrapper.vm.value).toBe('value') // 对异步获取的数据进行断言,判断获取的值与期望是否都相等
      done() // 使用done()结束回调
    })
  })
})
复制代码

上面的这个官方demo是通过调用组件内的接口,并对接口返回的数据进行断言。一开始我也照着官方给的代码使用,但是很快发现了一些随之出现的问题。

1. 调用组件本身的接口,意味着需要在组件内部传入接口请求时所需要的参数,这个数据我们可以自己传入创建的vue实例中,但是如果对于全部接口都自己传入数据会非常麻烦。而且还分成传入的参数正确或错误等不同情况。

2. 调用vue组件本身的http请求测试,必须得有后台接口配合,不能独立出来测试。而且测试会对后台产生真实的请求记录,因为我们开发过程中不知道会测试多少次,所以会给后台添加很多无用的请求记录。

3. 因为公司开发项目时,使用的是 docker 创建的虚拟容器作为运行环境,配置了网络地址转发,这使得我vue组件中的请求无法成功,也是这条因素使得我不能使用官方给的测试方法。

然后我就请教了我们的老大,在他的帮助下我使用另一种方法测试异步请求,做法是拦截组件中的异步请求,使用自己模拟的http请求。代码如下:

// 需要改造一下我们的 axios 请求
import axios from 'axios'
export getValue(...arg){
  return axios.get('mock/service',..arg).then(res=>{
    Promise.resolve({ date: 'value' })
  })
}
复制代码
// getValue.vue
<template>
  <button @click="fetchResults" />
</template>
<script>
  import getValue from 'axios'
  export default {
    data() {
      return {
        value: null
      }
    },
    methods: {
      fetchResults() {
        getValue(...arg).then(res=>{
          this.value = res.date
        })
      }
    }
  }
</script>
复制代码
// getValue.test.js
import { shallowMount } from '@vue/test-utils'
import * as svc from 'axios'
import Foo from './Foo'

describe('getValue.vue组件', () => {
  it('点击button时,异步获取接口返回的value值', done => {
    const getValue = jest.spyOn(svc, 'getValue') // 使用jest.spyOn()创建一个mock函数
    getValue.mockReturnValueOnce(Promise.resolve({data: 'value')) // 模拟我们自己mock函数的返回值
    const wrapper = shallowMount(Foo)
    wrapper.find('button').trigger('click') // 模拟用户点击事件
    wrapper.vm.$nextTick(() => { // 使用$nextTick 在Promise执行后再进行断言
      expect(getValue).toBeCalled(); // 断言是否请求了自己mock的getValue函数
      expect(wrapper.vm.value).toBe('value') // 对异步获取的数据进行断言,判断获取的值与期望是否都相等
      done() // 使用done()结束回调
    })
  })
})
复制代码

jest.spyOn()方法创建一个mock函数,这个mock的函数会在组件的接口请求的时候被执行,并返回我们给mock函数添加的返回值,通过判断这个mock函数是否被执行,以及组件获取的返回值与我们给mock函数添加的返回值是否相等就可以判断组件的异步请求是否能够正确执行。通过这种方式,我们来测试异步组件。

实际上我的同事,之前也写过一篇在react项目中使用jest测试的文章,其中也介绍了使用jest.spyOn()来测试异步请求的情况。感兴趣的话可以去这里结合了解一下。

3. 测试在有路由情况下的vue组件

vue 官方也给出了vue-test-util 配合 vue-router 使用的文档。我工作中出现的一个情况是要测试在某个路由地址下, <router-view> 加载的子组件的测试。但是到目前为止本人还没有按照官方的实例跑通过测试,很尴尬,也许是我打开的姿势不对,等之后正确实现之后会把方法再补上。下面我介绍一个对这种情况测试的非官方的写法。

import Vue from 'vue'
import VueRouter from 'vue-router'
import totest from 'src/components/totest'

describe('totest.vue', () => {
  it('should totest renders stuff', (done) => {
    Vue.use(VueRouter)
    const router = new VueRouter({routes: [ // 定义路由,其中使用了被测试组件
      {path: '/totest/:id', name: 'totest', component: totest},
      {path: '/wherever', name: 'another_component', component: {render: h => '-'}},
    ]})
    const vm = new Vue({ // 自己新建一个带 router 且有 router-view 的vue实例
      el: document.createElement('div'),
      router: router,
      render: h => h('router-view')
    })
    router.push({name: 'totest', params: {id: 123}}) // 使用测试组件的路由
    Vue.nextTick(() => {
      console.log('html:', vm.$el)
      expect(vm.$el.querySelector('h2').textContent).to.equal('Fred Bloggs');
      done()
    })
  })
})
复制代码

上面这个测试代码很巧妙的新建了一个使用router的vue实例,然后把被测试组件加到路由中去,当改变路由地址时,被测试组件就会被执行。此时可以对被测试组件进行断言。

4. 其他问题

在开发过程中还遇到了一些其他问题,如:

1. vue项目使用了element组件,测试代码报错,类似于 el-button 未注册

2. 项目使用了vue-i18n做国际化,测试代码报错,报错信息为 vm._$t is not a function...

3. 使用国际化vue-i18n的时候,vue组件代码里如果有 i18n.locale 的话测试代码会提示无法找到locale属性

..........

上面我列出来的这三个问题,并没有给出具体的解决办法。12解决比较简单,3目前还不知道如何处理...之后请教一下我们的大佬或者自己查阅下资料,等解决之后再来更新文章。因为每个人的项目不一样,可能我遇到的问题别人并不会遇到,所以还是对着报错自己查照调试吧。这也能更加完善你自己的项目代码。本人也是小白,第一次学习写测试代码,之后有了更深入的了解之后会更新这篇文章。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

数字乌托邦

数字乌托邦

尼古拉斯•卡尔 / 姜忠伟 / 中信前沿出版社 / 2018-5 / 69.00

当下,技术与我们的关系变得越来越紧密不可分割,特别是智能手机等设备的出现,带给整个人类社会一场彻底的变革。的确,智能手机上的各种应用程序让我们的工作生活无比便利:社交媒体让我们能够和他人实时保持联络并传输信息,不再受时间、地点的限制;搜索引擎通过精准的算法将我们所需要的信息整合推送至屏幕上,让我们毫不费力就看到自己想要的;地图软件为我们的出行提供了更多路线选择,甚至可以使用语音导航,帮助我们顺利到......一起来看看 《数字乌托邦》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试