详解使用jest对vue项目进行单元测试
本文详细记录了在Vue CLI 3.0环境下使用Jest进行单元测试的全过程。从基础配置入手,解决了静态图片资源在测试中返回对象而非字符串的问题,并通过jest-transform-stub和fileMock.js实现正确Mock。进一步演示了使用Vue Test Utils进行组件交互测试、快照测试,以及如何通过Manual Mocks模拟Axios插件和Store状态,最终生成完整的代码覆盖率报告。
Vue项目单元测试背景与准备
近期,团队对前端开发提出了引入单元测试的新要求。为了实践这一需求,我选择了之前使用Vue开发的“快报名”小程序PC端页面作为测试对象。虽然此前有过React项目的测试经验,但这是首次尝试在Vue项目中开展单元测试工作。
鉴于Vue CLI 3.0在单元测试支持上更为完善,我将项目升级至该版本。由于项目基于TypeScript开发,需要引入ts-jest配合使用,最终确定的Jest配置如下所示。
Jest基础配置与静态资源处理
{
"jest": {
"moduleFileExtensions": [
"js",
"jsx",
"json",
"vue",
"ts",
"tsx"
],
"transform": {
"^.+\\.vue$": "vue-jest",
".+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub",
"^.+\\.tsx?$": "ts-jest"
},
"moduleNameMapper": {
"^@/(.*)$": "<rootDir>/src/$1"
},
"snapshotSerializers": [
"jest-serializer-vue"
],
"testMatch": [
"**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"
],
"testURL": "http://localhost/"
}
}
首先从简单的正则字符串常量文件入手进行测试,结果非常顺利,没有任何报错。
随后开始对方案页面的Scheme.vue组件进行测试,重点在于验证一个computed属性。编写了三种具有代表性的测试用例后,运行yarn test:unit Scheme.test.ts,结果显示三个it测试用例均通过,但控制台却输出了红色的错误警告。
console.error node_modules/vue/dist/vue.runtime.common.js:589 [Vue warn]: Invalid prop: type check failed for prop "headerPic". Expected String, got Object.
排查发现,问题出在调用的一个子组件上,该组件期望接收一个headerPic属性作为图片的src地址。查看相关源码后,发现这里使用了require引入图片资源。
<SideNav :header-pic="require('../../assets/scheme/schemeSideNavPic.jpg')">
直觉上认为逻辑没有问题,通过vue-devtool检查,"/img/schemeSideNavPic.f988623b.jpg"确实显示为字符串,似乎并非require本身的问题。考虑到可能是Jest对require的处理机制导致,再次检查Jest配置,发现已经针对jpg等静态文件做了相应处理。查看jest-transform-stub模块的源码,实现非常简单。
module.exports = {
process: function() {
return ''
}
}
既然该模块对静态文件返回空字符串且不做额外处理,按理说不应导致类型错误。借助VSCode强大的调试功能,尝试调试源码,但未能在Vue文件中触发断点。无奈之下,在TypeScript文件中添加const pic = require('../../../assets/scheme/schemeSideNavPic.jpg')语句,再次启动调试,发现实际获取到的值。

正是jest-transform-stub模块的处理结果,确实是一个对象,这与命令行运行的结果一致。因此,需要调整处理方式,使其返回正确的字符串格式。
module.exports = ""
查阅Jest官方文档,搜索相关关键词,运气不错,moduleNameMapper选项完全能够满足处理静态文件的需求。
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js"
}
fileMock.js内容如下:
// __mocks__/fileMock.js
module.exports = 'test-file-stub';
这意味着只要该模块返回字符串即可解决问题。在配置中加入moduleNameMapper后,测试完美通过。
组件交互与快照测试实战
接下来,对Scheme.vue组件发起模拟点击事件的测试。
const createScheme = wrapper.findAll('.sn-item').at(1)
createScheme.trigger('click')
expect((wrapper.vm as any).isCreateDialogShow).toBeTruthy()
expect(wrapper.find('.create-list-dialog').isVisible()).toBeTruthy()
使用Vue Test Utils的API获取createScheme元素,并触发其点击事件,验证isCreateDialogShow这个data值是否被正确设置为true。该项目使用了element-ui组件库。
<el-dialog
:visible.sync="isCreateDialogShow"
width="600px"
class="create-list-dialog"
title="创建方案">
...
</el-dialog>
由于此dialog可见性逻辑正确,测试顺利通过。
随后尝试新功能——快照测试,使用toMatchSnapshot方法也顺利通过了验证。
复杂场景:Axios插件Mock与数据模拟
接下来进行更复杂的测试,针对Login.vue登录页面。主要测试其调用接口并成功设置Store值的过程。由于不能走真实的网络接口(速度慢且结果不可预测),必须使用Mock数据。
项目中创建了axios.plugin.ts Vue插件,这该如何Mock呢?查阅官方文档,感觉Manual Mocks部分最合适,但官方示例并不适用于Vue插件的Mock。继续浏览文档,不知是受何启发还是突然开窍,应该是受fs模块的启发,突然明白了如何Mock插件。Mock一个模块只需模仿其类型即可,具体实现无关紧要。这个HTTP请求插件的Mock必须能返回期望的值,fs模块的__setMockFiles方法给了我启示,可以直接给接口的返回result设值。于是有了下面的实现。
__mocks__/axios.plugin.ts文件内容如下:
const MockAxios = {} as any
let result = {} as any
MockAxios.install = (Vue: any, options: any) => {
Vue.prototype.$axios = function () {
/* eslint-disable prefer-promise-reject-errors */
return new Promise((resolve, reject) => {
if (result.ResultCode === '200') {
return result.Info
} else {
reject({ code: result.ResultCode, msg: result.Message, info: result.Info })
}
})
}
}
MockAxios.__setMockData = (data: any) => {
result = data
}
export default MockAxios
此后测试一路顺畅,执行localVue.use(Vuex)和localVue.use(AxiosPlugin)后,
const mockData = {
ResultCode: '200',
Msg: true,
Info: {
OpenId: 99,
UserId: 92003,
},
}
AxiosPlugin.__setMockData(mockData)
(wrapper.vm as any).login({ code: '29992' }).then(() => {
expect(wrapper.vm.$store.state.userInfo.OpenId).toBe(mockData.Info.OpenId)
expect(wrapper.vm.$store.state.userInfo.UserId).toBe(mockData.Info.UserId)
})
测试完美通过,Vue的单元测试框架基本搭建完成,可以向领导汇报进展了。
生成代码覆盖率报告
向领导展示成果时,还需要提供覆盖率报告。
yarn test:unit --coverage
当前覆盖的文件较少,未包含所有源文件。需要加入collectCoverageFrom配置项,至此整个单元测试体系就比较完备了。
下面是完整的Jest配置。
{
"jest": {
"moduleFileExtensions": [
"js",
"jsx",
"json",
"vue",
"ts",
"tsx"
],
"transform": {
"^.+\\.vue$": "vue-jest",
".+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub",
"^.+\\.tsx?$": "ts-jest"
},
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
"^@/(.*)$": "<rootDir>/src/$1"
},
"snapshotSerializers": [
"jest-serializer-vue"
],
"testMatch": [
"**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"
],
"testURL": "http://localhost/",
"collectCoverageFrom": [
"**/*.{vue,ts}",
"!**/node_modules/**",
"!**/*.d.ts"
]
}
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
JS压缩后代码报错怎么调试?Source Map使用详解
2026-11-05 09:05:04
JS压缩会破坏代码逻辑吗?兼容性风险与测试建议
2026-11-03 17:13:03
Next.js 与 Vite、Create React App 的优缺点对比
2026-11-03 15:27:33
js文件加载失败或404错误的常见原因与排查
2026-11-03 10:38:55
前端项目打包生成 JS 文件的核心步骤完整记录
2026-10-31 21:04:46








