分类
Javascript

使用vue cli单元测试

要保证前端的代码质量,单元测试是少不了的。特别是对核心业务的代码,要更详细地进行单元测试。之前使用Karma写过AngluarJS单元测试。现在vue项目越来越多,怎么写vue的单元测试呢?记得vue cli的webpack模板是支持单元测试的。下面我们就来简单介绍一下怎样使用使用vue cli中的jest运行器写单元测试。

一、目录结构

准备vue cli2项目。可以看到单元测试代码代码集中在test/unit文件夹中,其中jest.conf.js是jest的配置文件,请到jest官方了解更多。setup.js用来设置每个测试文件都引用的代码。.eslintrc用来覆盖添加整个项目的eslint设置。

官方推荐单元测试写在test/unit/specs下,这样便于集中管理,Jest测试运行器扫描也比较快。测试文件推荐以.spec.js结尾。项目中已经有HelloWorld.spec.js实例,是用来测试HelloWorld组件的。

HelloWorld.spec.js就一个用例,测试h1标签的内容是否正确。Jest写单元测试代码很简单,和Jasmine的BDD风格差不多。describe创建测试套件,it放置测试用例,expect放置断言语句。想了解更多,可参考我以前写Jasmine简介

二、运行测试

npm run unit运行一下,居然报错:SecurityError: localStorage is not available for opaque origins。Google了一下,原来vue cli项目中,jest默认的testEnvironmento为’jsdom’,jsdom升级导致不能向下兼容。具体可以查看jsdom issues。解决方案是在jest.conf.js中添加

testURL: "http://localhost"

再次运行,控制台看到下面的输出就说明测试通过了。

三、编写测试

不容易呀,npm再强大,包太多也管理不过来,这样的版本升级导致的Bug在前端的日常工作不小呀,幸好有万能的Google。下面我们自己动手就来编写一个组件的单元测试。毕竟“纸上得来终觉浅”,得勤动手。

以下是一个简单的单文件组件:

<template>
  <div>
    <span class="count">{{ count }}</span>
    <button @click="increment">Increment</button>
    <button @click="decrement">Decrement</button>
  </div>
</template>
<script>
export default {
  name: 'Counter',
  data () {
    return {
      count: 1
    }
  },
  methods: {
    increment () {
      this.count++
    },
    decrement () {
      this.count--
    }
  }
}
</script>

接下来我们来编写它的单元测试Counter.spec.js。

import Vue from 'vue'
import Counter from '@/components/Counter'

describe('Counter.vue', () => {
  const Constructor = Vue.extend(Counter)
  const vm = new Constructor().$mount()
  it('should render the correct tag', () => {
    const Constructor = Vue.extend(Counter)
    const vm = new Constructor().$mount()
    expect(vm.$el.innerHTML).toContain('<span class="count">1</span>')
  })

  it('should have 2 buttons', () => {
    expect(vm.$el.querySelectorAll('button')).toHaveLength(2)
  })

  it('count default value should one', () => {
    expect(vm.count).toBe(1)
  })
})

可以看到,我们引入了Vue和Counter组件,然后通过extend创建一个包含被挂载和渲染的Vue组件的实例,所有测试用例都是基于该实例来创建的。因此,我们将这部分代码放到用例代码块外共享。前面三个用例比较简单,基本上都是Dom元素或内容断言。

计数器的增减应该是测试的重点。不应该直接访问Vue组件的实例的方法来实现的,这种方式是反测试原则的,因为单元测试代码和组件代码耦合了。组件代码重构了,测试代码也要重写。我们应该通过点击按钮触发click事件来测试计数。但一时又找不到相关的API。百度好几篇文章几乎都是说不支持。后来Google了一下,在Github上找到了一个大佬在一次conf的演讲上用的示例代码。他是用原生代码派发自定义事件来实现的。虽然这个方案不够优雅,但还是能解决问题的,添加如下两个测试用例:

it('should increment the count', () => {
    const button = vm.$el.querySelectorAll('button')[0]
    const customEvent = new Event('click')
    button.dispatchEvent(customEvent)
    expect(vm.count).toBe(2)
  })

  it('should decrement the count', () => {
    const button = vm.$el.querySelectorAll('button')[1]
    const customEvent = new Event('click')
    button.dispatchEvent(customEvent)
    expect(vm.count).toBe(1)
  })

好了,这个组件基本上测试充分了,真是Happy。怎么我就没有想到的,虽然我的原生JS功底不错呢。

注意

vue cli单元测试是主要是通过jest、babel-jest、vue-jest这些包完成的,知道了这一 点,我们也可以手动这已有的vue项目搭建搭建单元测试环境。

Vue项目是先写测试瑞写代码?我认为在做鬼新项目而且时间足够的情况下使用TDD是最好的。但有的项目我们不得不赽进度或是中途参与进来的,先写代码再写测试也是可以理解的。

😀Happy Test!