博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
vue单元测试vue test utils使用初探
阅读量:6857 次
发布时间:2019-06-26

本文共 5518 字,大约阅读时间需要 18 分钟。

简介

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

1.什么是测试代码

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

  • 一个简单的vue组件的测试
// message.vue

上面这个最简单的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
复制代码

上面是一个带有简单交互的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
复制代码
// getValue.test.jsimport { 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
复制代码
// getValue.test.jsimport { 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目前还不知道如何处理...之后请教一下我们的大佬或者自己查阅下资料,等解决之后再来更新文章。因为每个人的项目不一样,可能我遇到的问题别人并不会遇到,所以还是对着报错自己查照调试吧。这也能更加完善你自己的项目代码。本人也是小白,第一次学习写测试代码,之后有了更深入的了解之后会更新这篇文章。

参考文章

原文链接:


作者简介: 宫晨光,人和未来大数据前端工程师。

转载于:https://juejin.im/post/5ca56c2551882543fa41d51f

你可能感兴趣的文章
OGC和Khronos建立合作关系,共同推动地理信息和3D图形社群的交互操作
查看>>
代码大全的总结
查看>>
欢迎来到JIT的世界: The Joy of Simple JITs
查看>>
android init 启动过程分析
查看>>
学习使用码云上传和下载代码并创建分期
查看>>
Sphinx2.2.11在windows7 下的安装使用
查看>>
AVFoundation 初解
查看>>
独立undo表空间在线回收
查看>>
IP转物理地址的原理
查看>>
使用POI解析Excel文件(支持97-03-07-10)
查看>>
浅析高权重网站依托的是什么
查看>>
git cherry-pick
查看>>
在 Mac上压缩的压缩包 在windows上都会产生.DS_Store这种垃圾文件 怎么办
查看>>
List、Map比较排序
查看>>
当税收遇到了电商 团购网站遭遇尴尬
查看>>
争做万年老二? 赶集网三线开战 打的过谁?
查看>>
Centos 系统初始化管理脚本
查看>>
ElementUI之formatter的使用
查看>>
rsync同步备份linux的用户资料
查看>>
单链表的创建和遍历、求单链表中节点的个数、查找单链表中的中间结点、判断单链表是否有环、取出有环链表中环的长度,删除有序链表中的重复结点...
查看>>