最近在作一个项目的重构,技术选型为vue-cli 3.0 + typescript + vue-router + sass.由于我负责的模块比较少比较简单,因此老大让我先把负责部分的测试代码写好。至此我才第一次接触到测试代码,咱们项目使用的测试工具是jest,与vue官方出的单元测试工具库vue-test-utils配合使用。第一次接触测试代码,开始的时候仍是一脸懵逼,有种学习一门新语言的赶脚。通过几天的摸索以后学会了简单的编写测试代码,并对几种状况进行特殊处理。本文是一篇vue单元测试的基础入门文章,只介绍测试代码,须要了解搭建测试框架的朋友能够自行参阅vue-test-utils官方文档等资料。javascript
简言之,测试代码就是经过代码模拟一个vue组件的运行环境,使被测试的组件在这个环境下运行看是否可以获得指望的运行结果。若是运行结果与指望的结果相同,则说明该测试用例经过。下面咱们来看一个最简单的实例:html
// 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',若是相等,则说明此断言为真。vue
// 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标签中的值即加一。对于这样的组件咱们的测试代码为java
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
})
})
复制代码
经过上面的例子咱们看到,测试代码能够模拟出用户的操做,经过对操做以后的结果进行断言,能判断出该组件可否经过测试。并且一个测试用例中能够有多个断言,只有所有断言经过才说明该组件实例经过测试。只要有一个断言未经过,则说明该组件实例未经过测试。react
在项目中咱们很常见到vue组件内向接口请求数据的状况,那么咱们如何测试这种异步请求呢。官方也给了咱们实例代码:ios
// 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是经过调用组件内的接口,并对接口返回的数据进行断言。一开始我也照着官方给的代码使用,可是很快发现了一些随之出现的问题。vue-router
而后我就请教了咱们的老大,在他的帮助下我使用另外一种方法测试异步请求,作法是拦截组件中的异步请求,使用本身模拟的http请求。代码以下:vue-cli
// 须要改造一下咱们的 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函数添加的返回值是否相等就能够判断组件的异步请求是否可以正确执行。经过这种方式,咱们来测试异步组件。docker
实际上个人同事,以前也写过一篇在react项目中使用jest测试的文章,其中也介绍了使用jest.spyOn()来测试异步请求的状况。感兴趣的话能够去这里结合了解一下。
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实例,而后把被测试组件加到路由中去,当改变路由地址时,被测试组件就会被执行。此时能够对被测试组件进行断言。
在开发过程当中还遇到了一些其余问题,如:
vm._$t is not a function...
i18n.locale
的话测试代码会提示没法找到locale属性..........
上面我列出来的这三个问题,并无给出具体的解决办法。12解决比较简单,3目前还不知道如何处理...以后请教一下咱们的大佬或者本身查阅下资料,等解决以后再来更新文章。由于每一个人的项目不同,可能我遇到的问题别人并不会遇到,因此仍是对着报错本身查照调试吧。这也能更加完善你本身的项目代码。本人也是小白,第一次学习写测试代码,以后有了更深刻的了解以后会更新这篇文章。
原文连接:tech.gtxlab.com/vue-test-ut…
做者简介: 宫晨光,人和将来大数据前端工程师。