文章原址: https://www.cnblogs.com/yalong/p/11714393.html
背景介紹:
以前寫的公共組件,后來需要添加一些功能,添加了好幾次,每次修改我都要測試好幾遍保證以前的功能不受影響,有次我測試遺漏導致組件出現bug,而且由於是公共組件,我每次修改還得讓其他小伙伴更新組件,很是麻煩,所以一定要寫測試, 對自己負責,也是對他人負責!
前端測試介紹
1.單元測試(unit測試)
單元測試是把代碼看成是一個個的組件,從而實現每一個組件的單獨測試,測試內容主要是組件內每一個函數的返回結果是不是和期望值一樣。
2.端到端測試(e2e測試)
e2e測試是把我們的程序堪稱是一個黑盒子,我不懂你內部是怎么實現的,我只負責打開瀏覽器,把測試內容在頁面上輸入一遍,看是不是我想要得到的結果。
兩者的存在都是很有意義的:
unit測試是程序員寫好自己的邏輯后可以很容易的測試自己的邏輯返回的是不是都正確。
e2e代碼是測試所有的需求是不是都可以正確的完成,而且最終要的是在代碼重構,js改動很多之后,需要對需求進行測試的時候測試代碼是不需要改變的,你也不用擔心在重構后不能達到客戶的需求。
對於公共組件單元測試就可以滿足基本需求了,本文也只講單元測試。
單元測試的好處
- 減少bug
- 提高代碼質量
- 快速定位問題,減少調試時間
- 單元測試還是一份很好的業務文檔,每項測試的描述都可以體現業務邏輯
- 降低人工測試的成本,雖然編寫及維護測試腳本需要付出額外的成本,但從長遠來看,這些成本通常遠比采用人工測試要低地多
- 保證該庫在后續的開發維護過程中不會出現意料之外的問題;在修改代碼「比如優化、重構、修改或添加新的功能等」后,往往需要重新進行測試,這時人工測試通常無法保證覆蓋到每一個測試點,這時就會為項目帶來隱患
不過不是所有的代碼都要寫單元測試,因為寫單元測試也是有一定工作量的,對於更新頻繁,變化較快的需求,讓前端寫測試,他估計會崩潰。但是對於公共組件,或者其他比較穩定的業務組件,單元測試是很有必要的。
采用 jest + Vue Test Utils進行單元測試的原因
1.Jest是 Facebook 的一套開源的 JavaScript 測試框架, 它自動集成了斷言、JSDom、覆蓋率報告等開發者所需要的所有測試工具,配置較少,對vue框架友好。
2.Vue Test Utils 是 Vue.js 官方的單元測試實用工具庫,為jest和vue提供了一個橋梁,暴露出一些接口,讓我們更加方便的通過Jest為Vue應用編寫單元測試。
3.vue-cli 默認的單元測試也是使用的這套方案
對於不了解Vue Test Utils 的同學可以先看這里 VueTestUtils, 想了解Jest 的同學可以看這里 Jest
在現有項目中添加(Jest + Vue Test Utils)的測試環境
為了演示配置的過程, 我用vue-cli 初始化了一個簡單的項目,webpack 是4.0的
項目代碼地址: 點我
1.安裝依賴npm i @vue/test-utils babel-jest jest jest-serializer-vue jest-transform-stub vue-jest -D
我的項目安裝好如下:
2.修改.babelrc配置
在根目錄的.babelrc中添加如下配置
"env": { "test": { "presets": ["env"] } }
就變成了如下(項目本身的配置不用改)
{
"presets": [
["env", { "modules": false }]
],
"env": {
"test": {
"presets": ["env"]
}
}
}
3.建立測試文件目錄
在根目錄下建立test目錄,test里面再按照如下建立對應文件,文件夾,圖上的紅字是注釋
4.添加jest配置,jest.conf.js內容如下,相關屬性的解釋也寫在了注釋里
const path = require('path'); module.exports = { verbose: true, testURL: 'http://localhost/', rootDir: path.resolve(__dirname, '../../'), moduleFileExtensions: [ 'js', 'json', 'vue' ], moduleNameMapper: { '^@\/(.*?\.?(js|vue)?|)$': '<rootDir>/src/$1', // @路徑轉換,例如:@/components/Main.vue -> rootDir/src/components/Main.vue '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/test/unit/__mocks__/fileMock.js', // 模擬加載靜態文件 '\\.(css|less|scss|sass)$': '<rootDir>/test/unit/__mocks__/styleMock.js' // 模擬加載樣式文件 }, testMatch: [ //匹配測試用例的文件 '<rootDir>/test/unit/specs/*.spec.js' ], transform: { '^.+\\.js$': '<rootDir>/node_modules/babel-jest', '.*\\.(vue)$': '<rootDir>/node_modules/vue-jest' }, testPathIgnorePatterns: [ '<rootDir>/test/e2e' ], // setupFiles: ['<rootDir>/test/unit/setup'], snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'], coverageDirectory: '<rootDir>/test/unit/coverage', // 覆蓋率報告的目錄 collectCoverageFrom: [ // 測試報告想要覆蓋那些文件,目錄,前面加!是避開這些文件 // 'src/components/**/*.(js|vue)', 'src/components/*.(vue)', '!src/main.js', '!src/router/index.js', '!**/node_modules/**' ] }
備注:
單元測試的思想是單純的測試組件,對於樣式,圖片等這些靜態資源是不予測試的,所以上面的配置中才有了對這些靜態資源進行了模擬加載,不然
Jest + Vue Test Util這倆哥們解析不了scss, css, img..這些靜態資源,測試就跑不起來了。
同時對於組件內引用的外部資源,也需要模擬,比如axios,下面的測試代碼里面有處理的演示。
5.給測試添加eslint配置,test/unit/ 目錄下的.eslintrc內容如下
{
"env": {
"jest": true
}
}
6.__mocks__文件目錄下建立 fileMock.js,用來處理測試中遇到的靜態資源, 內容就一行代碼
module.exports = 'test-file-stub';
- 在
specs下寫測試用例代碼,像下圖所示(組件名+spec):
- 在
package.json的scripts里添加測試命令
"unit": "jest --config test/unit/jest.conf.js --coverage"
執行 npm run unit 就可以啟動測試了,測試完畢會產生類似下圖的報告, 測試覆蓋率,測試用例,鏡像..都有
編寫測試用例
先看下我演示的項目,如下
checkbox 開關控制圖片的顯隱
表單請求有驗證,點擊立即創建觸發表單驗證,驗證通過提交表單;點擊重置按鈕去掉驗證提示。
我的組件就兩個
一個
Form.vue 一個
Main.vue, 就對這倆個組件測試。
測試用例寫了三個,如下
里面詳細的代碼我就不貼出來了,可以去項目源碼里面看。
下面說下寫這幾個測試用例需要注意的地方
1.由於項目用到了
element-ui 所以在寫測試用例的時候,也需要給模擬的
Vue(createLocalVue) install element-ui
關鍵部分的代碼如下:
import { mount, createLocalVue } from '@vue/test-utils'
const localVue = createLocalVue()
import ElementUI from 'element-ui'
localVue.use(ElementUI)
import Form from '@/components/Form'
// 測試表單請求
describe('Test Form Request', () => {
it('Form Request Sucess', () => {
let wrapper = mount(Form, {
stubs: {
transition: false
},
localVue
})
})
})
2.checkbox切換的時候,控制圖片顯示/隱藏,需要用nextTick
it('show switch img', () => {
wrapper.setData({ switchvalue: true })
// 修改完數據 dom操作沒同步 需要用 nextTick
return Vue.nextTick().then(function() {
expect(wrapper.findAll('.logoImg').length).toBe(1)
})
})
3.模擬axios
為什么要模擬axios ?
因為Jest + Vue Test Utils這套環境中是沒有
axios的,所以他不認axios, 但是組件代碼里面確實調用了axios, 那么我們就需要模擬一個axios出來
新建 axios.js 文件
axios.js 的內容如下:
module.exports = {
get: jest.fn(() => Promise.resolve({ status: 200 }))
}
我這里只用到了 status: 200,大家根據自己需求設置返回的數據。
測試用例代碼如下:
import { mount, createLocalVue } from '@vue/test-utils' import Vue from 'vue' const localVue = createLocalVue() import ElementUI from 'element-ui' localVue.use(ElementUI) import axios from 'axios' import Form from '@/components/Form' // 測試表單請求 describe('Test Form Request', () => { it('Form Request Sucess', () => { let wrapper = mount(Form, { stubs: { transition: false }, localVue, propsData: { initFormData: { name: '一起團建', type: ['地推活動'], desc: '吃喝玩樂' } } }) wrapper.find('.confirm').trigger('click') return Vue.nextTick().then(function() { expect(wrapper.vm.sucess).toBe(true) let url = 'http://rap2api.taobao.org/app/mock/233956/tbl-unit-test?name=' + wrapper.vm.ruleForm.name + '&nature=' + wrapper.vm.ruleForm.type.join(',') + '&form=' + wrapper.vm.ruleForm.form expect(axios.get).toBeCalledWith(url) }) }) })
以上介紹了單元測試,以及如何在現有項目基礎上添加測試的配置,並寫了幾個測試用例,應該可以讓不了解單元測試的同學入門了,更高級點的可以看 這里 這個項目里面的單元測試就比較全面,大家可以參考下。
演示項目代碼: https://github.com/YalongYan/vue-test-utils-jest
參考連接:
Vue Test Utils
Jest Using With Wbpack
使用jest對vue項目進行單元測試
ui組件如何進行單元測試
Jest Mock
用Jest測試Vue中的Methods中的方法和Mock依賴
