Vue单元测试踩坑 持续更新... - junruchen/junruchen.github.io GitHub Wiki

单元测试的重要性,在这里就不过多阐述了。

这篇文章主要记录在vue项目中引入单元测试遇到的一些问题以及解决方案。

项目背景:

引入一些第三方库,如:

  • element-ui组件库
  • echarts图表库

测试内容:

  • utils中的公共方法,工具函数的正确性与异常处理
  • components中的公共组件,组件是否正确的输入与输出
  • filters中的处理是否正确
  • 等等

测试工具:

命令配置:

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;')
  })
})

踩坑记

1、对于使用了element子组件的组件,测试时,提示子组件找不到

方法一: 在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
})

2、项目中有时需要在一个公共函数中使用element子组件,如message消息提示等,此时测试,会报关于css解析的错误,如下图:

1.png

解决方案:

在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'

3、如果项目中引入了lodash-es组件,且jest默认不会解析node_modules,这时候就需要进行额外的配置了:

moduleNameMapper: {
  'lodash-es': 'lodash'
}
⚠️ **GitHub.com Fallback** ⚠️