一、为什么要做单元测试
试想下这样的场景,你写了一个公共的组件,团队里有多个项目依赖它;某一天你发现了一个bug,改完之后仅仅验证了该bug的功能,或许你根本没有时间验证是否影响了其它的地方,这个时候你提交了,并且知会多个项目更新组件。 不幸的是当大家更新之后,发现你改的bug引发了其它的问题,这个时候你肯定很想骂人~
领导知道之后,让你要确保后续组件的质量,你会怎么做呢?
-
自己多做测试,但是由于组件的场景复杂,每次修改都要验证所有功能,简直浪费时间,估计你不太愿意~
-
交给测试同学,但是测试同学也会提出以上问题,也不会为了你改的一个小bug或者小功能就全量测试~
-
交给单元测试,执行npm run test,简直太酷了,接了一杯水回来发现,测试通过了~
你领导看后,肯定让你选择方案三吧。
既然领导都发话了,接下来你不得不去调研如何对Vue单文件组件进行单元测试了。
二、Vue Test Utils
通过查资料,你知道了Vue官方提供了Vue Test Utils这个工具库辅助你对单文件组价进行单元测试。
先看下Vue Test Utils是干吗的?
Vue Test Utils 会将单文件组件隔离挂载,然后模拟必要的输入 (prop、注入和用户事件) 和 对输出 (渲染结果、触发的自定义事件) 的断言来测试 Vue 组件。
被挂载的组件会返回到一个包裹器内,而包裹器会暴露很多封装、遍历和查询其内部的 Vue 组件实例的便捷的方法。
我们想一下,要实现单文件的挂载至少需要哪些基本条件?
-
需要挂载的DOM(可以是浏览器环境的DOM或者运行在Node上的虚拟浏览器环境的jsDom),由于浏览器环境比较复杂,我们选择jsDom;
-
需要先将单文件组件进行编译,这样jsDom才能正常运行;
-
Vue。
有了以上三个条件,Vue Test Utils就可以将单文件组件生成HTML和JavaScript逻辑代码了。
三、选择一个测试运行器
测试运行器是执行测试集的程序。主流的JavaScript测试运行器比较多,且Vue Test Utils都支持。Vue Test Utils是与测试运行器无关的。
那我们该如何从众多测试运行器选择呢?需要关注以下几点:
- 测试运行器给我们提供的功能集合是否足够强大
- 性能
- 对单文件组价预编译的支持
Vue Test Utils推荐以下两个测试运行器
-
Jest是功能最全的测试运行器。它需要的配置是最少的,默认安装了JSDOM,内置断言命令行的用户体验非常好。 不过需要一个将Vue单文件组件预处理器,这样Jest才可以处理。Vue Test Utils为我们提供了vue-jest预处理器来处理最常见的单文件组件特性,注意不是100%的vue-loader.
-
mocha-webpack是一个webpack + Mocha的包裹器。同时包含了更顺畅的接口和侦听模式。这些设置的好处是我们可以通过webpack+vue-loader得到完整的单文件组件支持,但是需要很多配置。
这里我们选择的测试运行器是Jest;
四、使用Vue test Utils和Jest单元测试的原理
通过以上介绍,我们大致了解到使用Vue test Utils和Jest对Vue单文件组件进行单元测试的流程大概如下图所示:
五、搭建单元测试环境
那我们的环境要依赖哪些库呢?
- Jest
- vue-jest
- vue test utils
- babel-jest(因为我们需要将ES6以上转成ES5)
1、Jest @vue/test-utils
对Vue2项目推荐以下版本,可以直接运行起来
npm i -D @vue/test-utils@1.3.0 jest@24.9.0
配置文件
{
"scripts": {
"test": "jest"
}
}
2、Vue-jest
vue-jest是用来处理单文件组件的
npm i -D vue-jest@4.0.1
配置文件
{
// ...
"jest": {
"moduleFileExtensions": [
"js",
"json",
// 告诉 Jest 处理 `*.vue` 文件
"vue"
],
"transform": {
// 用 `vue-jest` 处理 `*.vue` 文件
".*\\.(vue)$": "vue-jest"
}
}
}
3、babel-jest
尽管最新版本的 Node 已经支持绝大多数的 ES2015 特性,你可能仍然想要在你的测试中使用 ES modules 语法和 stage-x 的特性。为此我们需要安装 babel-jest:
npm i -D babel-jest@24.9.0
配置如下:
{
transform: {
"^.+\\.js$": "<rootDir>/node_modules/babel-jest"
}
}
4、 babel-bridge
如果你的Babel版本高于7还需安装 babel-bridge
npm install --save-dev babel-core@^7.0.0-bridge.0
5、处理 webpack 别名
如果你在 webpack 中配置了别名解析,比如把 @ 设置为 /src 的别名,那么你也需要用 moduleNameMapper 选项为 Jest 增加一个匹配配置:
{
// ...
"jest": {
// ...
// 支持源代码中相同的 `@` -> `src` 别名
"moduleNameMapper": {
"^@/(.*)$": "<rootDir>/src/$1"
}
}
}
6、完整的jest.conf.js的配置如下:
// ./test/unit/jest.conf.js
const path = require('path');
module.exports = {
rootDir: path.resolve(__dirname, '../../'), // 类似 webpack.context
"collectCoverage": true,
moduleFileExtensions: [ // 类似 webpack.resolve.extensions
'js',
'json',
'vue',
],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1', // 类似 webpack.resolve.alias
},
transform: { // 类似 webpack.module.rules
'^.+\\.js$': '<rootDir>/node_modules/babel-jest',
// '.*\\.(vue)$': '<rootDir>/node_modules/vue-jest',
".*\\.(vue)$": '<rootDir>/node_modules/vue-jest',
},
setupFiles: ['<rootDir>/test/unit/setup'], // 类似 webpack.entry
coverageDirectory: '<rootDir>/test/unit/coverage', // 类似 webpack.output
collectCoverageFrom: [ // 类似 webpack 的 rule.include
'src/components/__test__/*.{js}',
'!src/main.js',
'!src/router/index.js',
'!**/node_modules/**',
],
transformIgnorePatterns: ['/node_modules/']
};
目录结构如下:

- setup.js入口文件
import Vue from 'vue'
Vue.config.productionTip = false;
这样单元测试的配置文件就完成了~
7、npm run test
"scripts": {
"test": "jest --config test/unit/jest.conf.js --coverage"
}
六、总结
环境配置好之后,命令行npm run test之后我们就可以看到测试结果,这样就回答了开头领导的如何保证组件的质量问题。至于如何写单元测试用例,将会在下一篇讲解~