如何为vue的项目添加单元测试


Posted in Javascript onDecember 19, 2018

动机

  • 单元测试能避免出现一些代码运行结果与预期不符的错误,通常是一些比较低级但又难以发现的问题。
  • 粗心且懒,在每次调整之后,需要不断地检查代码,反复去走流程。担心由于自己的改动而导致了逻辑上的错误。而这里面的一大部分工作其实可以让单元测试来完成。
  • 有了单元测试之后,可以对代码本身形成一种规范。如果在进行单元测试过程中发现自己的一些代码不方便进行测试,那么你可能需要重新审视这些代码,看是否有一些设计上不合理或者可以优化的地方。
  • 嵌入了单元测试的项目显得更加的专业,也会更有逼格,测试本身是开发环节需要做的内容。

工具选取对比(一个合适测试框架 -- Jest)

之前也没有去接触过前端的单元测试,也是这几天开始了解,开始并没有头绪,所以就在网上以及github上去看了一些之前比较流行的测试框架。发现比较流行的是karma + mocha + Chrome的组合。当我单独一个个去看的时候,发现其内容还是比较的多的。之后选取了jest也是经过对比权衡的

优点

1、一站式的解决方案,学习成本更低,上手更快(很适合现如今我的需求)

在使用 Jest 之前,我需要一个测试框架(mocha),需要一个测试运行器(karma),需要一个断言库(chai),需要一个用来做 spies/stubs/mocks 的工具(sinon 以及 sinon-chai 插件),一个用于测试的浏览器环境(可以是 Chrome 浏览器,也可以用 PhantomJS)。 而使用 Jest 后,只要安装它,全都搞定了。

2、全面的官方文档,易于学习和使用

Jest 的官方文档很完善,对着文档很快就能上手。而在之前,我需要学习好几个插件的用法,至少得知道 mocha 用处和原理吧 我得学会 karma 的配置和命令,chai 的各种断言方法……,经常得周旋于不同的文档站之间,其实是件很烦也很低效的事

3、更直观明确的测试信息提示

4、方便的命令行工具

缺点

jsdom 的一些局限性:因为 Jest 是基于 jsdom 的,jsdom 毕竟不是真实的浏览器环境,它在测试过程中其实并不真正的“渲染”组件。这会导致一些问题,例如,如果组件代码中有一些根据实际渲染后的属性值进行计算(比如元素的 clientWidth)就可能出问题,因为 jsdom 中这些参数通常默认是 0.

综上所述,最终我确定下来的方案是使用成熟好用的测试工具库 --- vue-test-utils 其前身是 avoriaz,avoriaz 也是一个不错的包,但其 README 中有说明,当 vue-test-utils 正式发布的时候, 它将会被废弃。 vue-test-utils 能极大地简化 Vue.js 单元测试。 例如:Vue 单元测试,一般是像下面这样的(包括 vue-cli 提供的模板里默认也是这样):

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

describe('HelloWorld.vue', () => {
 it('should render correct contents', () => {
  const Constructor = Vue.extend(HelloWorld)
  const vm = new Constructor().$mount()
  expect(vm.$el.querySelector('.hello h1').textContent)
   .toEqual('Welcome to Your Vue.js App')
 })
})

使用 vue-test-utils 后,你可以像下面这样

import { shallow } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld'

describe('HelloWorld.vue', () => {
 it('should render correct contents', () => {
  const wrapper = shallow(HelloWorld, {
   attachToDocument: ture
  })

  expect(wrapper.find('.hello h1').text()).to.equal('Welcome to Your Vue.js App')
 })
})

可以看到代码更加简洁了。wrapper 内含许多有用的方法,上面的例子中所使用的 find() 其中最简单不过的一个。vue-test-utils 还有 createLocalVue() 等方法以及 stub 之类的功能,基本上可以完成绝大部分情况下的测试用例,这也是非常的实用的了。

安装使用

安装使用的方式很简单,由于想引入到现有的项目中来,现有的项目大多是vue-cli创建的,所以一开始的时候基本上是已经安装并配置好了 webpack、vue-loader 和 Babel。如果是比较原始的项目,也是可以单独安装的。

我们要做的第一件事就是安装 Jest 和 Vue Test Utils:

$ npm install --save-dev jest @vue/test-utils

然后我们需要在 package.json 中定义一个单元测试的脚本。

// package.json
{
 "scripts": {
  "test": "jest"
 }
}

在 Jest 中处理单文件组件

npm install --save-dev vue-jest

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

{
 // ...
 "jest": {
  "moduleFileExtensions": [
   "js",
   "json",
   // 告诉 Jest 处理 `*.vue` 文件
   "vue"
  ],
  "transform": {
   // 用 `vue-jest` 处理 `*.vue` 文件
   ".*\\.(vue)$": "vue-jest"
  }
 }
}

具体的使用步骤

此处我根据自己的需求来进行整理

对页面内容的测试

// viewTest.vue
<template>
 <div class="hello">
  <h1>{{ msg }}</h1>
  <p>1212121</p>
 </div>
</template>

<script>
export default {
 name: 'viewTest',
 data () {
  return {
   msg: 'Welcome to Your Vue.js App'
  }
 }
}
// viewTest.spec
import { mount } from '@vue/test-utils'
import Component from '../../../src/components/viewTest'

describe('页面展示测试', () => {
 test('检查元素是否存在', () => {
  const wrapper = mount(Component)
  expect(wrapper.contains('.hello h1')).toBe(true)
  console.log(wrapper.find('.hello h1').text())
  expect(wrapper.text()).toContain('Welcome')
 })
})

这个是最简单的对页面的dom节点的测试,以及可以对文案进行一些测试,这些是比较基础的

对事件处理的测试

// event.vue
<template>
   <div>
    <h1>My To Do event</h1>
    <h2>wawawawawawa</h2>
    <input v-model="newItem">
    <button @click="addItemToList">Add</button>
    </br>
    <!--displays event -->
    <ul>
     <li v-for="item in listItems">{{ item }}</li>
    </ul>
   </div>
  </template>

  <script>
  export default {
   name: 'event',
   data () {
    return {
     listItems: ['buy food', 'play games', 'sleep'],
     newItem: ''
    }
   },
   methods: {
    addItemToList() {
     this.listItems.push(this.newItem);
     this.newItem = '';
    }
   }
  }
  </script>
// event.spec.js
// 从测试实用工具集中导入 `mount()` 方法
// 同时导入你要测试的组件
import { mount } from '@vue/test-utils'
import Component from '../../../src/components/itemEvent'

describe('事件触发测试', () => {
 test('事件触发测试', () => {
 // 现在挂载组件,你便得到了这个包裹器
  const wrapper = mount(Component)
  const button = wrapper.find('button')
  wrapper.setData({
   newItem: '添加测试项',
  })
  button.trigger('click')
  console.log(wrapper.text())
  expect(wrapper.text()).toContain('添加测试项')
 })
})

这里是在模拟用户交互的一个测试,当用户点击按钮的时候会把数据插入到当前的列表中来,所以最开始需要定位到这个按钮,可以用find(),之后要去触发这个事件, button.trigger('click'),然后把预期的结果,与按照流程的结果相比较,以达到测试的效果。这里模拟的是一个点击事件,当然,api也支持各种的鼠标事件以及键盘事件。

测试异步行为 平时的业务场景中肯定是离不开异步操作的,当发送一个接口请求的时候应该怎么去才做。Jest 运行测试用例同时可以模拟了 HTTP 库 axios,对预期结果可以进行设定和比较,比如:

// axios.js
export default {
 get: () => Promise.resolve({ data: 'response' })
}
<template>
 <div>
  <button @click="fetchResults">发送请求</button>
  {{value}}
 </div>

</template>

<script>
import axios from '../axios.js'

export default {
 data () {
  return {
   value: '初始值'
  }
 },
 methods: {
  async fetchResults () {
   const response = await axios.get('mock/service')
   this.value = response.data
   console.log(this.value)
  }
 }, 
 created (){
  console.log(axios.get)
 }
}
</script>
// async.spec.js
import { shallowMount } from '@vue/test-utils'
import async from '../../../src/components/async'
jest.mock('axios')

it('当点击按钮发送请求时检验返回值', () => {
 const wrapper = shallowMount(async)
 console.log(jest)
 wrapper.find('button').trigger('click')
 // expect(wrapper.value)
 expect(wrapper.vm.value).toBe('response')
 // console.log(wrapper.vm.value).toBe('初始值')
})

这个时候运行的话会报错误

如何为vue的项目添加单元测试

因为断言在 fetchResults 中的 Promise 完成之前就被调用了,所以value的值还是最开始的初始值。大多数单元测试库都提供一个回调来使得运行期知道测试用例的完成时机。Jest 和 Mocha 都是用了 done。我们可以和 $nextTick 或 setTimeout 结合使用 done 来确保任何 Promise 都会在断言之前完成。

  • 测试 Vue Router 使用

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Javascript 阻止javascript事件冒泡,获取控件ID值
Jun 27 Javascript
服务器端的JavaScript脚本 Node.js 使用入门
Mar 07 Javascript
jquery显示隐藏input对象
Jul 21 Javascript
node.js中的buffer.write方法使用说明
Dec 10 Javascript
浅谈js中对象的使用
Aug 11 Javascript
jQuery页面弹出框实现文件上传
Feb 09 Javascript
浅谈js for循环输出i为同一值的问题
Mar 01 Javascript
Angular.JS中的指令引用template与指令当做属性详解
Mar 30 Javascript
JS使用正则表达式验证身份证号码
Jun 23 Javascript
vue中动态绑定表单元素的属性方法
Feb 23 Javascript
基于JavaScript实现瀑布流布局
Aug 15 Javascript
微信小程序:报错(in promise) MiniProgramError
Oct 30 Javascript
浅谈Angular7 项目开发总结
Dec 19 #Javascript
mockjs+vue页面直接展示数据的方法
Dec 19 #Javascript
vue项目搭建以及全家桶的使用详细教程(小结)
Dec 19 #Javascript
vue使用Google地图的实现示例代码
Dec 19 #Javascript
JS实现获取自定义属性data值的方法示例
Dec 19 #Javascript
vue动态绑定class选中当前列表变色的方法示例
Dec 19 #Javascript
js指定日期增加指定月份的实现方法
Dec 19 #Javascript
You might like
ThinkPHP实现非标准名称数据表快速创建模型的方法
2014/11/29 PHP
jQuery 获取对象 定位子对象
2010/05/31 Javascript
js 数据类型转换总结笔记
2011/01/17 Javascript
JavaScript判断密码强度(自写代码)
2013/09/06 Javascript
判断客户浏览器是否支持cookie的示例代码
2013/12/23 Javascript
JavaScript日期类型的一些用法介绍
2015/03/02 Javascript
jQuery DOM删除节点操作指南
2015/03/03 Javascript
JavaScript制作颜色反转小游戏
2016/09/25 Javascript
解决拦截器对ajax请求的拦截实例详解
2016/12/21 Javascript
bootstrap中模态框、模态框的属性实例详解
2017/02/17 Javascript
在javaScript中检测数据类型的几种方式小结
2017/03/04 Javascript
jQuery表单设置值的方法
2017/06/30 jQuery
详解Vue2.0配置mint-ui踩过的那些坑
2018/04/23 Javascript
详解js跨域请求的两种方式,支持post请求
2018/05/05 Javascript
详解vue-router 初始化时做了什么
2018/06/11 Javascript
socket在egg中的使用实例代码详解
2019/05/30 Javascript
如何在wxml中直接写js代码(wxs)
2019/11/14 Javascript
[01:06:59]完美世界DOTA2联赛PWL S2 Magma vs FTD 第一场 11.29
2020/12/02 DOTA
python僵尸进程产生的原因
2017/07/21 Python
Python批处理删除和重命名文件夹的实例
2018/07/11 Python
python实现整数的二进制循环移位
2019/03/08 Python
python系列 文件操作的代码
2019/10/06 Python
python PIL/cv2/base64相互转换实例
2020/01/09 Python
Python3使用xlrd、xlwt处理Excel方法数据
2020/02/28 Python
python+selenium+Chrome options参数的使用
2020/03/18 Python
Django实现celery定时任务过程解析
2020/04/21 Python
python利用paramiko实现交换机巡检的示例
2020/09/22 Python
IE矩阵Matrix滤镜旋转与缩放及如何结合transform
2012/11/29 HTML / CSS
利用CSS3实现进度条的两种姿势详解
2017/03/21 HTML / CSS
解决Firefox下不支持outerHTML问题代码分享
2014/06/04 HTML / CSS
中国跨境海淘网站:考拉海购
2016/08/01 全球购物
delegate与普通函数的区别
2014/01/22 面试题
艺术专业大学生自我评价
2013/09/22 职场文书
回门宴父母答谢词
2014/01/26 职场文书
教师申诉制度
2014/01/29 职场文书
提高系统的吞吐量解决数据库重复写入问题
2022/04/23 MySQL