文章原址:https://www.cnblogs.com/yalong/p/11714393.html

背景介绍:

以前写的公共组件,后来需要添加一些功能,添加了好几次,每次修改我都要测试好几遍保证以前的功能不受影响,有次我测试遗漏导致组件出现bug,而且由于是公共组件,我每次修改还得让其他小伙伴更新组件,很是麻烦,所以一定要写测试, 对自己负责,也是对他人负责!

前端测试介绍

1.单元测试(unit测试)
单元测试是把代码看成是一个个的组件,从而实现每一个组件的单独测试,测试内容主要是组件内每一个函数的返回结果是不是和期望值一样。
2.端到端测试(e2e测试)
e2e测试是把我们的程序堪称是一个黑盒子,我不懂你内部是怎么实现的,我只负责打开浏览器,把测试内容在页面上输入一遍,看是不是我想要得到的结果。

两者的存在都是很有意义的:

unit测试是程序员写好自己的逻辑后可以很容易的测试自己的逻辑返回的是不是都正确。
e2e代码是测试所有的需求是不是都可以正确的完成,而且最终要的是在代码重构,js改动很多之后,需要对需求进行测试的时候测试代码是不需要改变的,你也不用担心在重构后不能达到客户的需求。

对于公共组件单元测试就可以满足基本需求了,本文也只讲单元测试。

单元测试的好处

  • 减少bug
  • 提高代码质量
  • 快速定位问题,减少调试时间
  • 单元测试还是一份很好的业务文档,每项测试的描述都可以体现业务逻辑
  • 降低人工测试的成本,虽然编写及维护测试脚本需要付出额外的成本,但从长远来看,这些成本通常远比采用人工测试要低地多
  • 保证该库在后续的开发维护过程中不会出现意料之外的问题;在修改代码「比如优化、重构、修改或添加新的功能等」后,往往需要重新进行测试,这时人工测试通常无法保证覆盖到每一个测试点,这时就会为项目带来隐患

不过不是所有的代码都要写单元测试,因为写单元测试也是有一定工作量的,对于更新频繁,变化较快的需求,让前端写测试,他估计会崩溃。但是对于公共组件,或者其他比较稳定的业务组件,单元测试是很有必要的。

采用 jest + Vue Test Utils进行单元测试的原因

1.Jest是 Facebook 的一套开源的 JavaScript 测试框架, 它自动集成了断言、JSDom、覆盖率报告等开发者所需要的所有测试工具,配置较少,对vue框架友好。

2.Vue Test Utils 是 Vue.js 官方的单元测试实用工具库,为jest和vue提供了一个桥梁,暴露出一些接口,让我们更加方便的通过Jest为Vue应用编写单元测试。

3.vue-cli 默认的单元测试也是使用的这套方案

对于不了解Vue Test Utils 的同学可以先看这里 VueTestUtils, 想了解Jest 的同学可以看这里 Jest

在现有项目中添加(Jest + Vue Test Utils)的测试环境

为了演示配置的过程, 我用vue-cli 初始化了一个简单的项目,webpack 是4.0

项目代码地址: 点我

1.安装依赖
npm i @vue/test-utils babel-jest jest jest-serializer-vue jest-transform-stub vue-jest -D
我的项目安装好如下:

 

2.修改.babelrc配置
在根目录的.babelrc中添加如下配置

"env": {
"test": {
"presets": ["env"]
}
}

就变成了如下(项目本身的配置不用改)

{
"presets": [
["env", { "modules": false }]
],
"env": {
"test": {
"presets": ["env"]
}
}
}

3.建立测试文件目录
在根目录下建立test目录,test里面再按照如下建立对应文件,文件夹,图上的红字是注释

4.添加jest配置,jest.conf.js内容如下,相关属性的解释也写在了注释里

const path = require('path');

module.exports = {
verbose: true,
testURL: 'http://localhost/',
rootDir: path.resolve(__dirname, '../../'),
moduleFileExtensions: [
'js',
'json',
'vue'
],
moduleNameMapper: {
'^@\/(.*?\.?(js|vue)?|)$': '<rootDir>/src/$1', // @路径转换,例如:@/components/Main.vue -> rootDir/src/components/Main.vue
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/test/unit/__mocks__/fileMock.js', // 模拟加载静态文件
'\\.(css|less|scss|sass)$': '<rootDir>/test/unit/__mocks__/styleMock.js'  // 模拟加载样式文件
},
testMatch: [ //匹配测试用例的文件
'<rootDir>/test/unit/specs/*.spec.js'
],
transform: {
'^.+\\.js$': '<rootDir>/node_modules/babel-jest',
'.*\\.(vue)$': '<rootDir>/node_modules/vue-jest'
},
testPathIgnorePatterns: [
'<rootDir>/test/e2e'
],
// setupFiles: ['<rootDir>/test/unit/setup'],
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
coverageDirectory: '<rootDir>/test/unit/coverage', // 覆盖率报告的目录
collectCoverageFrom: [ // 测试报告想要覆盖那些文件,目录,前面加!是避开这些文件
// 'src/components/**/*.(js|vue)',
'src/components/*.(vue)',
'!src/main.js',
'!src/router/index.js',
'!**/node_modules/**'
]
}

备注:

单元测试的思想是单纯的测试组件,对于样式,图片等这些静态资源是不予测试的,所以上面的配置中才有了对这些静态资源进行了模拟加载,不然Jest + Vue Test Util 这俩哥们解析不了scss, css, img..这些静态资源,测试就跑不起来了。
同时对于组件内引用的外部资源,也需要模拟,比如axios,下面的测试代码里面有处理的演示。

5.给测试添加eslint配置,test/unit/ 目录下的.eslintrc内容如下

{
"env": {
"jest": true
}
}

6.__mocks__文件目录下建立 fileMock.js,用来处理测试中遇到的静态资源, 内容就一行代码

module.exports = 'test-file-stub';
  1. specs下写测试用例代码,像下图所示(组件名+spec):
         
  1. package.json 的 scripts 里添加测试命令
"unit": "jest --config test/unit/jest.conf.js --coverage"

执行 npm run unit 就可以启动测试了,测试完毕会产生类似下图的报告, 测试覆盖率,测试用例,镜像..都有

编写测试用例

先看下我演示的项目,如下

checkbox 开关控制图片的显隐
表单请求有验证,点击立即创建触发表单验证,验证通过提交表单;点击重置按钮去掉验证提示。

我的组件就两个

一个Form.vue 一个 Main.vue, 就对这俩个组件测试。

测试用例写了三个,如下

里面详细的代码我就不贴出来了,可以去项目源码里面看。
下面说下写这几个测试用例需要注意的地方
1.由于项目用到了element-ui 所以在写测试用例的时候,也需要给模拟的Vue(createLocalVue) install element-ui
关键部分的代码如下:

import { mount, createLocalVue  } from '@vue/test-utils'
const localVue = createLocalVue()
import ElementUI from 'element-ui'
localVue.use(ElementUI) import Form from '@/components/Form' // 测试表单请求
describe('Test Form Request', () => {
it('Form Request Sucess', () => {
let wrapper = mount(Form, {
stubs: {
transition: false
},
localVue
})
})
})

2.checkbox切换的时候,控制图片显示/隐藏,需要用nextTick

it('show switch img', () => {
wrapper.setData({ switchvalue: true })
// 修改完数据 dom操作没同步 需要用 nextTick
return Vue.nextTick().then(function() {
expect(wrapper.findAll('.logoImg').length).toBe(1)
})
})

3.模拟axios
为什么要模拟axios ?

因为Jest + Vue Test Utils这套环境中是没有 axios的,所以他不认 axios, 但是组件代码里面确实调用了axios, 那么我们就需要模拟一个 axios 出来

新建 axios.js 文件

 

axios.js 的内容如下:

module.exports = {
get: jest.fn(() => Promise.resolve({ status: 200 }))
}

我这里只用到了 status: 200,大家根据自己需求设置返回的数据。
测试用例代码如下:

import { mount, createLocalVue  } from '@vue/test-utils'
import Vue from 'vue'
const localVue = createLocalVue()
import ElementUI from 'element-ui'
localVue.use(ElementUI)
import axios from 'axios' import Form from '@/components/Form' // 测试表单请求
describe('Test Form Request', () => {
it('Form Request Sucess', () => {
let wrapper = mount(Form, {
stubs: {
transition: false
},
localVue,
propsData: {
initFormData: {
name: '一起团建',
type: ['地推活动'],
desc: '吃喝玩乐'
}
}
})
wrapper.find('.confirm').trigger('click')
return Vue.nextTick().then(function() {
expect(wrapper.vm.sucess).toBe(true)
let url = 'http://rap2api.taobao.org/app/mock/233956/tbl-unit-test?name=' + wrapper.vm.ruleForm.name + '&nature=' + wrapper.vm.ruleForm.type.join(',') + '&form=' + wrapper.vm.ruleForm.form
expect(axios.get).toBeCalledWith(url)
})
})
})

以上介绍了单元测试,以及如何在现有项目基础上添加测试的配置,并写了几个测试用例,应该可以让不了解单元测试的同学入门了,更高级点的可以看 这里 这个项目里面的单元测试就比较全面,大家可以参考下。

演示项目代码: https://github.com/YalongYan/vue-test-utils-jest

参考连接:
Vue Test Utils
Jest Using With Wbpack
使用jest对vue项目进行单元测试
ui组件如何进行单元测试
Jest Mock
用Jest测试Vue中的Methods中的方法和Mock依赖

前端单元测试,以及给现有的vue项目添加jest + Vue Test Utils的配置的更多相关文章

  1. 如何为你的 Vue 项目添加配置 Stylelint

    如何为你的 Vue 项目添加配置 Stylelint 现在已经是 9102 年了,网上许多教程和分享帖都已经过期,照着他们的步骤来会踩一些坑,如 stylelint-processor-html 已经 ...

  2. Vue项目添加动态浏览器头部title

    0. 直接上 预览链接 + 效果图 Vue项目添加动态浏览器头部title 1. 实现思路 ( 1 ) 从路由router里面得到组件的title ( 2 ) title存vuex (本项目已经封装h ...

  3. 为 VUE 项目添加 PWA 解决发布后刷新报错问题

    为什么要给 VUE 项目添加 PWA 为什么要添加?因为不管是部署在 IIS,还是 nginx,每次应用部署后,再次访问因为旧的 js 已经不存在,所以页面访问的时候会整个报错,报错的结果就是一个白屏 ...

  4. Vue项目无法使用局域网IP直接访问的配置方法

    一般使用 vue-cli 下来的项目是可以直接访问局域网 IP 打开的,比如 192.168.1.11:8080 .但是最近公司的一个项目只可以通过 localhost 访问. 需要配置一下,才可直接 ...

  5. Vue 项目添加单元测试发现的问题及解决

    用 Jest 测试单文件组件 1.安装 Jest 和 Vue Test Utils npm install --save-dev jest @vue/test-utils 2.配置 package.j ...

  6. GitHub Vue项目推荐|Vue+Element实现的电商后台管理系统功能丰富

    GitHub Vue项目推荐|mall-admin-web是一个电商后台管理系统的前端项目基于Vue+Element实现 主要包括商品管理.订单管理.会员管理.促销管理.运营管理.内容管理.统计报表. ...

  7. 搭建vue项目并启动vue项目

    链接地址:https://blog.csdn.net/aa792978017/article/details/82939483 Vue.js是现在比较优秀的Web前端框架,下面开始从零开始搭建一个Vu ...

  8. 简单vue项目脚手架(vue+webpack2.0+vuex+vue-router)

    github地址 使用技术栈 webpack(^2.6.1) webpack-dev-server(^2.4.5) vue(^2.3.3) vuex(^2.3.1) vue-router(^2.5.3 ...

  9. pycharm中新建Vue项目时没有vue.js的解决办法

    可能很多小伙伴在使用pycharm 1,新建vue项目的时候并没有发现vue.js的名字, 2,新建.vue文件(即单文件组件)的时候没有 下面就来帮助大家一下,仅供参考 如图: 1.首先我们打开设置 ...

随机推荐

  1. 嵌入式Linux应用开发完全手册读书笔记——常用的命令

    嵌入式开发中常用的命令 grep命令 用法:grep [option] PATTERN [FILE...] 例如: 在内核目录下查找包含"request_irq"字样的文件 gre ...

  2. 算法---FaceNet在Tf下的实战篇

    FaceNet---Tensorflow下的下的实战篇 @WP20190225 ===============目录=============== 一.FaceNet算法简介 二.FaceNet配置与使 ...

  3. Jmeter - 命令行参数

    同步更新至个人博客:https://njlife.top/2019/07/12/Jmeter-%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0/ Jmeter ...

  4. wkhtmltopdf 自定义字体未生效或中文乱码

    使用wkhtmltopdf控件将网页保存成pdf的过程中出现网页中有些字体,在PDF中未生效.通过网上查询结果有一种处理方式: 在网页头部的style标签中,手工指定宋体字体的本地存放位置,wkhtm ...

  5. C# 泛型(4) 持续更新

    泛型可以创建独立于被包含类型的类和方法. C++模板与泛型相似. 泛型优点性能 System.Collections 和 System.Collections.Generic 名称空间泛型和非泛型集合 ...

  6. Kettle安装和简单使用

    Kettle安装和使用 安装 安装之前需要准备的环境为Java环境,需要提前配置好jdk 下载之后,解压即可使用. 使用 1.因为该工具主要是对数据库进行操作,所以需要提前将mysql的jar包放到l ...

  7. MyBatis中的OGNL教程

    MyBatis中的OGNL教程 有些人可能不知道MyBatis中使用了OGNL,有些人知道用到了OGNL却不知道在MyBatis中如何使用,本文就是讲如何在MyBatis中使用OGNL. 如果我们搜索 ...

  8. keil无法生成axf文件之解决方法

    参考:参考<鱼鹰单片机>https://blog.csdn.net/weixin_42876465/article/details/88356890 其实很简单 默认情况是生成 .axf ...

  9. bzoj4009: [HNOI2015]接水果(整体二分)

    题目描述 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果.由于她已经DT FC 了The big black, 她觉得这个游戏太简单了,于是发明了一个更加难的版本. 首先有 ...

  10. qt 防止应用重复启动

    QApplication a(argc, argv); QSharedMemory singleton(a.applicationName()); if(!singleton.create(1)) { ...