最近在作一個項目的重構,技術選型爲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…
做者簡介: 宮晨光,人和將來大數據前端工程師。