vue中如何使用jest單元測試
當我初次聽到單元測試時,心裡的第一感覺就兩個字nb,然後就是疑惑,這是啥,幹啥用,對代碼又有什麼幫助?接下來我會細細說一說我在學習以及應用單元測試的一些心得。(安裝教程不再敘述,按照文檔教程自行學習)
文檔推薦
學習新知識,有中文文檔當然是最好的啦!
- Vue Test Utils
- jest中文文檔
組件掛載相關方法
shallowMount
shallowMount:
創建一個包含被掛載和渲染的 Vue 組件的 Wrapper。與之對應的還有一個mount。
shallowMount與mount的區別:
在文檔中描述為"不同的是被存根的子組件",大白話就是shallowMount不會加載子組件,不會被子組件的行為屬性影響該組件。
為什麼使用shallowMount而不使用mount:
我認為單元測試的重點在"單元"二字,而不是"測試",想測試子組件再為子組件寫對應的測試代碼即可。
import { shallowMount } from '@vue/test-utils' import Foo from './Foo.vue' describe('Foo', () => { const wrapper = shallowMount(Foo) })
Wrapper(這裡隻記錄一些我經常使用的)
Wrapper
:Wrapper 是一個包括瞭一個掛載組件或 vnode,以及測試該組件或 vnode 的方法。Wrapper.vm
:這是該 Vue 實例。你可以通過 wrapper.vm 訪問一個實例所有的方法和屬性。Wrapper.classes
:返回是否擁有該class的dom或者類名數組。Wrapper.find
:返回第一個滿足條件的dom。Wrapper.findAll
:返回所有滿足條件的dom。Wrapper.html
:返回html字符串。Wrapper.text
:返回內容字符串。Wrapper.setData
:設置該組件的初始data數據。Wrapper.setProps
:設置該組件的初始props數據。Wrapper.trigger
:用來觸發事件。
<template> <div class="jest"> <div class="name">{{name}}</div> <div class="name">{{name}}{{text}}</div> <div class="text" @click="add">{{text}}</div> </div> </template> <script src="./script.js"> export default { name:"Foo", props:{ name:{ type: String, default: '王大大' } }, data() { return { text: 123 } }, methods:{ add(){ this.text += 1 } } } </script>
import { shallowMount } from '@vue/test-utils' import Foo from './Foo.vue' describe('Foo', () => { const wrapper = shallowMount(Foo) console.log(Wrapper.classes()) //['jest','name','test'] console.log(Wrapper.classes('jest')) //true console.log(Wrapper.find('name').text()) //返回第一個class是name的dom的內容 王大大 console.log(Wrapper.findAll('name')) //返回dom數組 console.log(Wrapper.findAll('name').at(0)) //取dom數組中的第一個 Wrapper.setData({text : 1}) console.log(Wrapper.vm.text) //1 Wrapper.setProps({name : "李大大"}) console.log(Wrapper.vm.name) //李大大 Wrapper.find('text').trigger("click") console.log(Wrapper.vm.text) // 2 })
掛載選項
就是shallowMount或者mount的時候可以設置一些初始化內容。具體可以初始化的全部內容請查詢文章開始的文檔。
import { shallowMount } from '@vue/test-utils' import Foo from './Foo.vue' const wrapper = shallowMount(Foo, { data() { return { bar: 'my-override' } }, propsData: { msg: 'aBC' } mocks: { $route: { query: { aaa: '1', } }, $router: { push: jest.fn(), replace: jest.fn(), } } })
jest-api
匹配器
使用不同匹配器可以測試輸入輸出的值是否符合預期。
toBe
:判斷是否相等toBeNull
:判斷是否為nulltoBeUndefined
:判斷是否為undefinedtoBeDefined
:與上相反toBeNaN
:判斷是否為NaNtoBeTruthy
:判斷是否為truetoBeFalsy
:判斷是否為falsetoContain
:數組用,檢測是否包含toHaveLength
:數組用,檢測數組長度toEqual
:對象用,檢測是否相等toThrow
:異常匹配(沒用過)
describe('Foo', () => { expect(2 + 2).toBe(4) expect(null).toBeNull() expect(undefined).toBeUndefined() let a = 1; expect(a).toBeDefined() a = 'ada'; expect(a).toBeNaN() a = true; expect(a).toBeTruthy() a = false; expect(a).toBeFalsy() a = [1,2,3]; expect(a).toContain(2) expect(a).toHaveLength(3) a = {a:1}; expect(a).toEqual({a:1}) })
mock函數
例如:在一個vue組件中,ajax請求、路由跳轉是常常用到的,但是在單測代碼中,我們並不能正確的知道他是否執行並達到瞭預期的效果,這時就需要mock函數。在上文掛載選項中的mocks.$router就是一種mock函數的應用實例。
mock函數自然是要使用該函數,下面是一些函數的常用匹配器
toBeCalledTimes
:判斷函數被執行的次數toBeCalled
:判斷函數是否被執行
<template> <div class="jest" @click="go"></div> </template> <script src="./script.js"> export default { name:"Foo", methods:{ go(){ this.$router.push({path:"/a"}) } } } </script>
import { shallowMount } from '@vue/test-utils' import Foo from './Foo.vue' const wrapper = shallowMount(Foo, { mocks: { $router: { push: jest.fn(), replace: jest.fn(), } } }) wrapper.find(“jest").trigger("click"); expect(wrapper.vm.$router.push).toBeCalled(); wrapper.find(“jest").trigger("click"); expect(wrapper.vm.$router.push).toBeCalledTimes(2);
promise模擬
jest.mock('@root/api'); //模擬api請求函數 import {getList} from '@root/api'; const listJson = JSON.parse(require('fs').readFileSync('./mock/getList.json')); //引入mock好的json數據 getList.mockResolvedValue(Promise.resolve(listJson)); //模擬promise返回 getList().then(res => { expect(res).toEqual(listJson); })
為什麼要使用單測
說瞭這麼多,那為什麼要使用單測呢。在看完上面的描述我想大傢也是一頭鬱悶,單測有用嗎?答案當然是有。下面舉個例子:
<template> <div class="jest"></div> </template> <script src="./script.js"> import {getList} from '@root/api'; export default { name:"Foo", created(){ getList() .then(res => { this.initData(res) }) }, methods:{ initData(data){ data.name = '王大大'; } } } </script>
import { shallowMount } from '@vue/test-utils' import Foo from './Foo.vue' const wrapper = shallowMount(Foo)
這時候就會報錯data.name,因為data不一定有值。正確案例如下
<template> <div class="jest"></div> </template> <script src="./script.js"> import {getList} from '@root/api'; export default { name:"Foo", created(){ getList() .then(res => { this.initData(res) }) }, methods:{ initData(data = {}){ data.name = '王大大'; } } } </script>
說白瞭就是讓你的代碼邏輯更符合預期結果。
總結
目前在vue中用到的一些基本知識都已經總結完瞭,如果以後在工作中使用瞭新的api,時間充裕的情況下我會持續更新。以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- vue3使用vue-router的完整步驟記錄
- TDesign在vitest的實踐示例詳解
- Vue3編程流暢技巧使用setup語法糖拒絕寫return
- vue3配置router路由並實現頁面跳轉功能
- Mock.js的安裝與使用教程(擺脫後端同學的束縛)