一. Jest 是什么
由 Facebook 开发的一套用于前端 JavaScript 的测试框架。Jest 的一个理念是提供一套完整集成的“零配置”测试体验。
二. 配置
NPM 包
*Vue-Cli3.0 内置了 Babel7,关于需要 Babel 包的这边就不提及了。
- jest -
npm i ``babel-jest jest
- babel-jest -
npm i babel-jest babel-jest
自动编译 JavaScript 代码 - @vue/test-utils -
npm i @vue/test-utils
官方提供了 Vue 组件单元测试的方法 - vue-jest -
npm i ``vue-jest
变压器和源映射支持
VUE 单文件组件支持
- “@vue/cli-plugin-unit-jest”: “^3.11.0”,
- “@vue/test-utils”: “1.0.0-beta.29”,
- “babel-core”: “7.0.0-bridge.0”,
babel.config.js
我们假设 webpack 使用了
babel-preset-env
,这时默认的 Babel 配置会关闭 ES modules 的转译,因为 webpack 已经可以处理 ES modules 了。然而,我们还是需要为我们的测试而开启它,因为 Jest 的测试用例会直接运行在 Node 上。 同样的,我们可以告诉babel-preset-env
面向我们使用的 Node 版本。这样做会跳过转译不必要的特性使得测试启动更快。 为了仅在测试时应用这些选项,可以把它们放到一个独立的env.test
配置项中 (这会被babel-jest
自动获取)。
env: {
test: {
presets: [['@babel/preset-env', { targets: { node: 'current' } }]]
}
}
jest.config.js
module.exports = {
/** 是否为浏览器环境 */
browser: true,
/** 模块支持的后缀名 */
moduleFileExtensions: ['js', 'json', 'vue', 'jsx'],
/** 解析文件配置 */
transform: {
'^.+\\.js$': 'babel-jest',
'.*\\.(vue)$': 'vue-jest',
'^.+\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
'^.+\\.jsx?$': 'babel-jest'
},
/** 模块别名设置 */
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'\\.(css|less)$': 'identity-obj-proxy'
},
/** 测试文件匹配 */
testMatch: [
'**/__tests__/**/*.[jt]s?(x)',
'**/?(*.)+(spec).[tj]s?(x)',
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
],
/** 忽略文件 */
transformIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/dist/', '/node_modules/'],
/** 是否开启测试覆盖率统计 */
collectCoverage: false,
/** 测试覆盖率数据来源 */
collectCoverageFrom: ['<rootDir>/src/utils/**/*.{js,vue}', '!**/node_modules/**'],
/** 测试覆盖率报告默认格式 */
coverageReporters: ['html', 'text-summary'],
/** 测试覆盖率生成报告目录 */
coverageDirectory: '<rootDir>/test/unit/coverage',
/** 文件忽略的配置 */
watchPathIgnorePatterns: ['<rootDir>/node_modules', '<rootDir>/dist', '<rootDir>/static'],
/** 层次显示测试套件中每个测试的结果 */
// bail: true
verbose: true,
/** 在遇到第一个失败后就停止继续运行测试用例 */ snapshotSerializers: ['jest-serializer-vue'],
testURL: 'http://localhost/',
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname']
}
package.json “scripts”
"test": "jest",
"test:watch": "jest --watch",
"test:unit": "jest --coverage && npm run test:coverage",
"test:coverage": "open test/unit/coverage/index.html"
例子🌰
utils
import customFunc from '@/utils/customFunc'
/** 数组索引项互换方法 */
describe('customFunc.js', () => {
test('数组指定索引项移动方法,源数组 [1, 2, 3, 4, 5] 将下标 0 的元素移动到下标 3,目标数组为 [2, 3, 4, 1, 5]', () => {
const arrayBefore = [1, 2, 3, 4, 5]
const arrayAfter = [2, 3, 4, 1, 5]
const fromIndex = 0
const toIndex = 3
expect(customFunc.moveItem(arrayBefore, fromIndex, toIndex)).toStrictEqual(arrayAfter)
})
})
components
import { createLocalVue, shallowMount } from '@vue/test-utils'
import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import CommonBreadcrumb from '@/components/common/CommonBreadcrumb.vue'
import { Breadcrumb, BreadcrumbItem } from 'element-ui'
const localVue = createLocalVue()
localVue.use(ElementUI)
Vue.use(Breadcrumb)
Vue.use(BreadcrumbItem)
/** 挂载函数并返回已渲染的文本的工具函数 */
function getRenderedText(propsData) {
const wrapper = shallowMount(CommonBreadcrumb, {
propsData: {
fileBreadcrumb: propsData
}
})
return wrapper.text()
}
function renderedTextIndexOfParams(args, renderedText) {
return args.filter(ele => renderedText.indexOf(ele) === -1).length === 0
}
test('评估通用面包屑组件渲染文案', () => {
// 默认文件夹
expect(
renderedTextIndexOfParams(
['默认文件夹'],
getRenderedText([
{
pid: 0,
label: '默认文件夹'
}
])
)
).toBe(true)
// 默认文件夹/层级一
expect(
renderedTextIndexOfParams(
['默认文件夹', '层级一'],
getRenderedText([
{
pid: 0,
label: '默认文件夹'
},
{
pid: 1,
label: '层级一'
}
])
)
).toBe(true)
})