Vue单元测试踩坑 持续更新... - junruchen/junruchen.github.io GitHub Wiki
单元测试的重要性,在这里就不过多阐述了。
这篇文章主要记录在vue项目中引入单元测试遇到的一些问题以及解决方案。
引入一些第三方库,如:
- element-ui组件库
- echarts图表库
- utils中的公共方法,工具函数的正确性与异常处理
- components中的公共组件,组件是否正确的输入与输出
- filters中的处理是否正确
- 等等
- vue-test-utils
- 测试运行器选择的是Jest,内置断言库十分友好
coverage用于查看单元测试覆盖率
"lint": "vue-cli-service lint src tests/unit ",
"test": "vue-cli-service test:unit",
"test:watch": "vue-cli-service test:unit --watchAll",
"test:cov:open": "vue-cli-service test:unit --coverage && open coverage/lcov-report/index.html"
举例:日期格式化方法
/**
* 日期格式化
* @param {Number} time 时间戳
* @param {String} formatType 格式,默认YYYY/MM/DD hh:mm:ss
*/
function timeFormat(time, formatType = 'YYYY/MM/DD HH:mm:ss') {
const date = Number(time)
if (!dayjs(date).isValid()) return 'Date Error'
return dayjs(date).format(formatType)
}
简单测试:
import { timeFormat } from '@/utils'
describe('Utils:timeFormat', () => {
const d = new Date('2019-05-05 20:22:22').getTime()
it('if error', () => {
expect(timeFormat('time')).toBe('Date Error')
})
it('format', () => {
expect(timeFormat(d)).toBe('2019/05/05 20:22:22')
expect(timeFormat(d, 'YYYY-MM')).toBe('2019-05')
})
})
举例:简单的icon组件
export default {
name: 'Icon',
template: `
<svg
class="icon"
:style="iconSize"
aria-hidden="true">
<use :xlink:href="'#' + type"></use>
</svg>
`,
props: {
type: {
type: String,
required: true
},
size: {
type: Number,
default: 16
}
},
computed: {
iconSize: function() {
// rem相对顶级fontsize计算[14]
const proportion = ((1 * this.size) / 14).toFixed(2)
return {
width: proportion + 'rem',
height: proportion + 'rem'
}
}
}
}
简单测试:
import Iconfont from '@/components/global/Iconfont.vue'
import { shallowMount } from '@vue/test-utils'
describe('Iconfont component', () => {
it('props 默认值检查', () => {
const wrapper = shallowMount(Iconfont, {
propsData: {
type: 'ca-database'
}
})
expect(wrapper.vm.size).toBe(16)
expect(wrapper.html().includes('ca-database')).toBe(true)
})
it('根据size计算宽度与高度', async () => {
const wrapper = shallowMount(Iconfont, {
propsData: {
size: 20,
type: 'ca-database'
}
})
expect(wrapper.vm.iconSize.width).toBe('1.43rem')
expect(wrapper.vm.iconSize.height).toBe('1.43rem')
expect(wrapper.attributes('style')).toBe('width: 1.43rem; height: 1.43rem;')
})
})
方法一: 在tests目录下,引入setupFile.js,在这个文件中引入element
import Vue from 'vue'
import Element from 'element-ui/lib/element-ui.common.js'
Vue.use(Element)
在jest.config.js中,增加以下配置
setupFiles: [
'./tests/setupFile.js'
]
上述方案适用于项目大面积使用element组件时
方法二:,可使用createLocalVue,设置隔离,单独引入element
import { createLocalVue } from '@vue/test-utils'
import Element from 'element-ui/lib/element-ui.common.js'
// 创建一个扩展的 隔离的 `vue` 构造函数
const localVue = createLocalVue()
localVue.use(Element)
// 后续测试时,在挂载选项中传入 `localVue`
mount(Component, {
localVue
})
解决方案:
在jest.config.js的moduleNameMapper
中增加以下配置:
moduleNameMapper: {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "test-file-stub",
"\\.(css|less)$": "identity-obj-proxy"
},
注意identity-obj-proxy
需要安装
另外在引用组件的地方更改引入的方式,如:import Message from 'element-ui/lib/message.js'
moduleNameMapper: {
'lodash-es': 'lodash'
}