前端单元测试,以及给现有的vue项目添加jest + Vue Test Utils的配置
文章原址: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';
- 在
specs
下写测试用例代码,像下图所示(组件名+spec):

- 在
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的配置的更多相关文章
- 如何为你的 Vue 项目添加配置 Stylelint
如何为你的 Vue 项目添加配置 Stylelint 现在已经是 9102 年了,网上许多教程和分享帖都已经过期,照着他们的步骤来会踩一些坑,如 stylelint-processor-html 已经 ...
- Vue项目添加动态浏览器头部title
0. 直接上 预览链接 + 效果图 Vue项目添加动态浏览器头部title 1. 实现思路 ( 1 ) 从路由router里面得到组件的title ( 2 ) title存vuex (本项目已经封装h ...
- 为 VUE 项目添加 PWA 解决发布后刷新报错问题
为什么要给 VUE 项目添加 PWA 为什么要添加?因为不管是部署在 IIS,还是 nginx,每次应用部署后,再次访问因为旧的 js 已经不存在,所以页面访问的时候会整个报错,报错的结果就是一个白屏 ...
- Vue项目无法使用局域网IP直接访问的配置方法
一般使用 vue-cli 下来的项目是可以直接访问局域网 IP 打开的,比如 192.168.1.11:8080 .但是最近公司的一个项目只可以通过 localhost 访问. 需要配置一下,才可直接 ...
- Vue 项目添加单元测试发现的问题及解决
用 Jest 测试单文件组件 1.安装 Jest 和 Vue Test Utils npm install --save-dev jest @vue/test-utils 2.配置 package.j ...
- GitHub Vue项目推荐|Vue+Element实现的电商后台管理系统功能丰富
GitHub Vue项目推荐|mall-admin-web是一个电商后台管理系统的前端项目基于Vue+Element实现 主要包括商品管理.订单管理.会员管理.促销管理.运营管理.内容管理.统计报表. ...
- 搭建vue项目并启动vue项目
链接地址:https://blog.csdn.net/aa792978017/article/details/82939483 Vue.js是现在比较优秀的Web前端框架,下面开始从零开始搭建一个Vu ...
- 简单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 ...
- pycharm中新建Vue项目时没有vue.js的解决办法
可能很多小伙伴在使用pycharm 1,新建vue项目的时候并没有发现vue.js的名字, 2,新建.vue文件(即单文件组件)的时候没有 下面就来帮助大家一下,仅供参考 如图: 1.首先我们打开设置 ...
随机推荐
- sql 拼接字符串单条拆分多条
SELECT * FROM ( SELECT A.WS_ID , B.NEXT_OPERATOR FROM ( SELECT WS_ID , [NEXT_OPERATOR] = CONVERT(XML ...
- Springboot静态资源映射 “/” 引发的血案
因为少写一个 / 浪费已个下午的时间,
- 信息: JSF1048:有 PostConstruct/PreDestroy 注释。标有这些注释的 ManagedBeans 方法将表示注释已处理。
在Myeclipse运行项目时,控制台会输出如下信息,但是项目正常运行,没有异常,还不知道怎么解决 信息: JSF1048:有 PostConstruct/PreDestroy 注释.标有这些注释的 ...
- 第一节:python读取excel文件
写在前面: (1)Excel中数字格式int(1),读出的是float(1.0)类型,导致传参时造成不同,强制转换时,int(str(1.0))在2.7版本又会报错ValueError: invali ...
- c#系统泛型委托
Action<T> 无返回值的系统泛型委托 namespace ConsoleApp1 { public class UserInfo { public int Id { get; set ...
- Nginx之什么是反向代理(一)
什么是反向代理? 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求 ...
- idea如何打开右侧工具栏
- sheet.getRow(rowIndex);为null_POI导出excel
第一次使用POI,出现这个问题,看到有其他猿也遇到过,不知道怎么处理,所以记录一下~ sheet.getRow(rowIndex);通过sheet获取指定行,rowIndex代表第几行 用rowInd ...
- Jquery调用Ajax实现联动使用json
在很多时候我们都会使用到联动.jquery.js是一个不错的js框架.其ajax也挺不错.下面将实现一个js联动:选择公司出来受益人.根据公司不同受益人不同. 前提是:你用引入jquery.js &l ...
- 01 salt平台,软件架构图
1.前期调研 1.别人家的 https://www.cnblogs.com/ssyfj/p/9060367.html#top https://www.cnblogs.com/evilliu/artic ...