rider-weex项目引入jest单元测试

王孙娜

weex开发到现在,我们一直在找机会启用单元测试,但由于需求繁多,时间和精力不太允许。在weex的开发过程中,除了产品需求,我们也一直在做公共组件的开发,由于公共组件的日渐增多,个人认为在公共组件内进行单元测试还是有一定的必要性。

开年那段时间需求不多,正好研究了下rider-weex项目引入单元测试。

根据官方的一些文档以及自己开发过程中的经验,我选择了jest作为测试运行器。vue官网上对它的评价是:

Jest是功能最全的测试运行器。它所需的配置是最少的,默认安装了 JSDOM,内置断言且命令行的用户体验非常好。

话休饶舌,开撸!

首先,安装全家桶

npm install --save-dev jest @vue/test-utils vue-jest babel-jest babel-preset-env  

接下来在 package.json中创建一个 jest 块:

"jest": {
    "moduleFileExtensions": [
      "js",
      "vue"// 告诉 Jest 处理 `*.vue` 文件
    ],
    "transform": {
      // 用 `vue-jest` 处理 `*.vue` 文件
      ".*\\.(vue)$": "vue-jest",
      // 用 `babel-jest` 处理 js
      "^.+\\.js$": "<rootDir>/node_modules/babel-jest"
    },
    "moduleNameMapper": {
      // 处理 webpack 别名,如果你在 webpack 中配置了别名解析,那么你也需要用 moduleNameMapper 选项为 Jest 增加一个匹配配置
      "^views/(.*)$": "<rootDir>/src/views/$1",
      "^util/(.*)$": "<rootDir>/src/util/$1",
      "^components/(.*)$": "<rootDir>/src/components/$1",
      "^__mocks__/(.*)$": "<rootDir>/test/__mocks__/$1"
    }
 }

scripts里增加test命令,colors会显示颜色,coverage会显示覆盖率报告

"test": "jest --colors --coverage",

修改.babelrc文件

{
  "presets": [["env", { "modules": false }], "es2015", "stage-0"],
  "plugins": [
    [
      "transform-runtime",
      "component",
      {
        "libraryName": "weex-ui",
        "libDir": "packages",
        "style": false
      }
    ]
  ],
  "env": {
    "test": {
      "presets": [["env", { "targets": { "node": "current" } }]],
    }
  },
}

这里会有个大坑~

babel的安装版本不能为7以上,否则会报

.plugins[0][1] must be an object, false, or undefined

错误

特别要注意的是,高版本的babel-jest里也会安装7以上的babel,注意安装23.6.0以内的babel-jest

然后,我们来写一个最简单的测试文件。

在主目录下,新建文件夹test/components,再新建dwdDatetimePicker.test.js文件

dwdDatetimePicker是一个日期组件

// dwdDatetimePicker.test.js

// 从测试实用工具集中导入 `shallowMount()` 方法
// 同时导入你要测试的组件以及组件中用到的函数
import { shallowMount } from '@vue/test-utils'  
import dwdDatetimePicker from 'components/dwdDatetimePicker/dwdDatetimePicker'  
import { parseDate, getYears, getMonths, getDays, getHours, getMinutes, formatDate, arrayIndexOf } from 'components/dwdDatetimePicker/util';  
import weex from 'weex-vue-render'

describe('dwdDatetimePicker', () => {  
  // 现在挂载组件,你便得到了这个包裹器
  const wrapper = shallowMount(dwdDatetimePicker)
  ...
 }

接下去,我们开始写断言,关于jest的一些用法,可以去它的官网学习https://jestjs.io/docs/zh-Hans/23.x/getting-started.html

setProps(props)可以设置Wrapper vmprop 并强制更新。你也可以传递一个 propsData 对象,这会用该对象来初始化 Vue示例

具体关于Vue Test Utils的API,可以去这里查看

test('打开dwdDatetimePicker', () => {  
    wrapper.setProps({
      show: true
    })
    // 你可以通过 `wrapper.vm` 访问实际的 Vue 实例
    // 当show为true时,overlayShow和pickerShow也改为true
    expect(wrapper.vm.overlayShow).toBeTruthy()

    expect(wrapper.vm.pickerShow).toBeTruthy()
 })

可以查看渲染是否正确

test('设置title', () => {  
    wrapper.setProps({
      title: '标题',
    })
    // 找到class为header-center-text的text,并且内容应该为`标题`
    expect(wrapper.find('text.header-center-text').text()).toBe('标题')
})

设置了年月日后,是否显示正确

test('设置value', () => {  
    wrapper.setProps({
      show: true,
      value: '2018-09-10',
    })

    expect(wrapper.vm.currentYear).toBe(18)
    expect(wrapper.vm.currentMonth).toBe(8)
    expect(wrapper.vm.currentDay).toBe(9)
})

触发点击交互

Wrapper暴露了一个trigger方法。它可以用来触发DOM事件

每个挂载的包裹器都会通过其背后的Vue 实例自动记录所有被触发的事件。你可以用wrapper.emitted()方法取回这些事件记录。

test('点击confirm', () => {  
    wrapper.find('div.header-right-view').trigger('click')
    // dwdConfirm方法有被触发
    expect(wrapper.emitted().dwdConfirm).toBeTruthy()
    // 并且数据为2018-10-10
    expect(wrapper.emitted().dwdConfirm[0]).toEqual(['2018-10-10'])
})

完整的测试文件示例:

// dwdDatetimePicker.test.js

// 从测试实用工具集中导入 `shallowMount()` 方法
// 同时导入你要测试的组件以及组件中用到的函数
import { shallowMount } from '@vue/test-utils'  
import dwdDatetimePicker from 'components/dwdDatetimePicker/dwdDatetimePicker'  
import { parseDate, getYears, getMonths, getDays, getHours, getMinutes, formatDate, arrayIndexOf } from 'components/dwdDatetimePicker/util';  
import weex from 'weex-vue-render'


describe('dwdDatetimePicker', () => {  
  // 现在挂载组件,你便得到了这个包裹器
  const wrapper = shallowMount(dwdDatetimePicker)

  // 触发断言
  test('打开dwdDatetimePicker', () => {
    wrapper.setProps({
      show: true
    })
    expect(wrapper.vm.overlayShow).toBeTruthy()

    expect(wrapper.vm.pickerShow).toBeTruthy()
  })
  test('设置title', () => {
    wrapper.setProps({
      title: '标题',
    })

    expect(wrapper.find('text.header-center-text').text()).toBe('标题')
  })

  test('设置value', () => {
    wrapper.setProps({
      show: true,
      value: '2018-09-10',
    })

    expect(wrapper.vm.currentYear).toBe(18)
    expect(wrapper.vm.currentMonth).toBe(8)
    expect(wrapper.vm.currentDay).toBe(9)
  })

  test('设置startDate,endDate', () => {
    wrapper.setProps({
      show: true,
      value: '2018-09-10',
      startDate: '2018-10-10',
      endDate: '2019-10-10',
    })

    expect(wrapper.vm.currentYear).toBe(0)
    expect(wrapper.vm.currentMonth).toBe(0)
    expect(wrapper.vm.currentDay).toBe(0)
  })

  test('点击confirm', () => {
    wrapper.find('div.header-right-view').trigger('click')
    expect(wrapper.emitted().dwdConfirm).toBeTruthy()
    expect(wrapper.emitted().dwdConfirm[0]).toEqual(['2018-10-10'])
  })

  test('点击cancel', () => {
    wrapper.find('div.header-left-view').trigger('click')
    expect(wrapper.emitted().dwdCancel).toBeTruthy()
  })

  test('测试util', () => {
    expect(parseDate('YYYY-MM-DD', '2018-01-01', 'v-model日期和format格式不匹配')).toEqual({
      year: '2018',
      month: '01',
      day: '01',
      hour: undefined,
      minute: undefined,
      noon: undefined
    })

    expect(getYears({year: '2018',month: '01',day: '01'}, {year: '2019',month: '01',day: '01'})).toEqual([
      {
        title: '2018年',
        value: 2018
      },
      {
        title: '2019年',
        value: 2019
      }
    ])

    expect(arrayIndexOf([1, 2], 1)).toBe(0)
  })
})

最后,跑npm run test

image 测试通过

覆盖率如下 image

本人才疏学浅,此文仅作为抛砖引玉,期待有更好的技术方案