文章目录
- 前言
- 一、直接创建一个继承jest的vue项目
- 1. 创建项目
- 2. 运行查看测试结果
- 二、vue2已有项目中手动配置
- 1. 安装jest和Vue Test Utils
- 2. 安装vue-jest处理单文件组件
- 2. 安装babel
- 3. 添加.babelrc文件
- 4. 安装babel-jest
- 5. 安装@vue/compiler-dom
- 6. 最终package.json
- 7. 写一个例子试试
- 二、vue3项目使用jest
- 1. 安装基本的包
- 1)jest包
- 2)安装jsdom的包
- 3) 安装测试vue组件涉及的包
- 4)安装test-utils
- 2.配置
- 1)新建jest.config.mjs
- 2) tsconfig.json添加jest
- 3)写一个单例测试并运行查看
前言
最近公司在打算以后规范写单元测试的,之前只知道后端有这个,然后就上网翻了翻前端怎么写单元测试,查到了jest这个框架,这篇主要说下我想要使用组件测试时候踩得坑,以及几种方式
这里我使用的是vue项目
一、直接创建一个继承jest的vue项目
先安装vue-cli,当然没安装的小伙伴可以自行百度下,我这里只介绍安装选择的配置
1. 创建项目
运行命令vue create my-app
来创建vue项目,找到如图Unit Test这项,选中,回车进行下一步
这里选择jest
项目创建好之后就可以看见这个项目已经具备jest
2. 运行查看测试结果
运行命令yarn test:unit
可以看到运行成功
当然也可以将它估计改成不匹配的来查看结果
二、vue2已有项目中手动配置
可能有的小伙伴手里已经有了项目,不允许重新创建,那么这个方法可能更适合你
官方文档
1. 安装jest和Vue Test Utils
因为单元测试只在dev环境使用所以按在dev下即可 两个包用空格隔开,可以同时安两个包()
npm install --save-dev jest @vue/test-utils
在 package.json添加命令
2. 安装vue-jest处理单文件组件
运行命令
npm install --save-dev vue-jest
添加好配置
如果有jest.config.js这个文件,就不要写在package.json,写在jest.config.js这个文件里,这个我一会在vue3项目中使用时介绍
2. 安装babel
npm install --save-dev babel-core@^7.0.0-bridge.0
npm install --save-dev @babel/core@7.4.5 @babel/preset-env@7.4.5
3. 添加.babelrc文件
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
]
]
}
4. 安装babel-jest
npm install --save-dev babel-jest
5. 安装@vue/compiler-dom
npm install --save-dev @vue/compiler-dom
6. 最终package.json
最后可能会因为一些版本什么会发生一些奇妙的错误,我直接把我的package.json贴出来帮大家排个坑
{
"name": "v2-demo",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"test": "jest"
},
"dependencies": {
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
"core-js": "^3.6.5",
"vue": "^2.6.11",
"vue-router": "3.5.3"
},
"devDependencies": {
"@babel/core": "7.4.5",
"@babel/plugin-transform-runtime": "^7.18.0",
"@babel/preset-env": "7.4.5",
"@vue/cli-plugin-babel": "~4.5.13",
"@vue/cli-plugin-eslint": "~4.5.13",
"@vue/cli-service": "~4.5.13",
"@vue/compiler-dom": "^3.2.34",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/server-test-utils": "1",
"@vue/test-utils": "^1.0.0-beta.29",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.1.0",
"babel-jest": "21.2.0",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-vue": "^6.2.2",
"jest": "21.2.1",
"jest-environment-jsdom-fifteen": "^1.0.2",
"jest-environment-jsdom-global": "^3.1.1",
"jest-serializer-vue": "^2.0.2",
"jsdom": "^19.0.0",
"prettier": "^2.2.1",
"vue-jest": "^3.0.4",
"vue-template-compiler": "^2.6.14"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended",
"@vue/prettier"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"jest": {
"testMatch": [
"**/tests/**/*.[jt]s?(x)",
"**/?(*.)+(spec|test).[tj]s?(x)",
"(/__tests__/.*|(\\.|/)(test|spec))\\.(js|ts)$"
],
"verbose": true,
"collectCoverage": false,
"collectCoverageFrom": [
"src/**/*.{js,ts,vue}"
],
"moduleFileExtensions": [
"js",
"json",
"vue"
],
"snapshotSerializers": ["jest-serializer-vue"],
"transform": {
"^.+\\.vue$": "vue-jest",
"^.+\\.js$": "babel-jest"
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
7. 写一个例子试试
// Import the `mount()` method from Vue Test Utils
import {mount} from '@vue/test-utils'
// The component to test
const MessageComponent = {
template: '<p>{{ msg }}</p>',
props: ['msg']
}
test('displays message', () => {
// mount() returns a wrapped Vue component we can interact with
const wrapper = mount(MessageComponent, {
propsData: {
msg: 'Hello world'
}
})
// Assert the rendered text of the component
expect(wrapper.text()).toContain('Hello world')
})
二、vue3项目使用jest
我是直接用的开源框架添加,这里主要介绍在vben admin添加
1. 安装基本的包
这些包安装后面都有–dev 主要是只在开发环境使用
1)jest包
这一步主要是使用jest涉及到的
yarn add jest ts-jest @types/jest --dev
2)安装jsdom的包
这一步主要是为了支持写涉及到dom的测试
yarn add jest-environment-jsdom jest-environment-jsdom-global --dev
3) 安装测试vue组件涉及的包
yarn add @vue/vue3-jest jest-serializer-vue --dev
4)安装test-utils
yarn add @vue/test-utils@next --dev
老规矩我还是贴出我的package.json
{
"name": "vben-admin",
"version": "2.8.0",
"author": {
"name": "vben",
"email": "anncwb@126.com",
"url": "https://github.com/anncwb"
},
"scripts": {
"bootstrap": "pnpm install",
"serve": "npm run dev",
"dev": "vite",
"build": "cross-env NODE_ENV=production vite build && esno ./build/script/postBuild.ts",
"build:test": "cross-env vite build --mode test && esno ./build/script/postBuild.ts",
"build:no-cache": "pnpm clean:cache && npm run build",
"report": "cross-env REPORT=true npm run build",
"type:check": "vue-tsc --noEmit --skipLibCheck",
"preview": "npm run build && vite preview",
"preview:dist": "vite preview",
"log": "conventional-changelog -p angular -i CHANGELOG.md -s",
"clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
"clean:lib": "rimraf node_modules",
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
"lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged",
"test:unit": "jest",
"test:gzip": "npx http-server dist --cors --gzip -c-1",
"test:br": "npx http-server dist --cors --brotli -c-1",
"reinstall": "rimraf pnpm-lock.yaml && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
"prepare": "husky install",
"gen:icon": "esno ./build/generate/icon/index.ts"
},
"dependencies": {
"@ant-design/colors": "^6.0.0",
"@ant-design/icons-vue": "^6.1.0",
"@iconify/iconify": "^2.2.1",
"@logicflow/core": "^1.1.13",
"@logicflow/extension": "^1.1.13",
"@vue/runtime-core": "^3.2.33",
"@vue/shared": "^3.2.33",
"@vueuse/core": "^8.3.0",
"@vueuse/shared": "^8.3.0",
"@zxcvbn-ts/core": "^2.0.1",
"ant-design-vue": "^3.2.0",
"axios": "^0.26.1",
"codemirror": "^5.65.3",
"cropperjs": "^1.5.12",
"crypto-js": "^4.1.1",
"dayjs": "^1.11.1",
"echarts": "^5.3.2",
"intro.js": "^5.1.0",
"lodash-es": "^4.17.21",
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
"path-to-regexp": "^6.2.0",
"pinia": "2.0.12",
"print-js": "^1.6.0",
"qrcode": "^1.5.0",
"qs": "^6.10.3",
"resize-observer-polyfill": "^1.5.1",
"showdown": "^2.1.0",
"sortablejs": "^1.15.0",
"tinymce": "^5.10.3",
"vditor": "^3.8.13",
"vue": "^3.2.33",
"vue-i18n": "^9.1.9",
"vue-json-pretty": "^2.0.6",
"vue-router": "^4.0.14",
"vue-types": "^4.1.1",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@commitlint/cli": "^16.2.3",
"@commitlint/config-conventional": "^16.2.1",
"@iconify/json": "^2.1.30",
"@purge-icons/generated": "^0.8.1",
"@types/codemirror": "^5.60.5",
"@types/crypto-js": "^4.1.1",
"@types/fs-extra": "^9.0.13",
"@types/inquirer": "^8.2.1",
"@types/intro.js": "^3.0.2",
"@types/jest": "^27.5.1",
"@types/lodash-es": "^4.17.6",
"@types/mockjs": "^1.0.6",
"@types/node": "^17.0.25",
"@types/nprogress": "^0.2.0",
"@types/qrcode": "^1.4.2",
"@types/qs": "^6.9.7",
"@types/showdown": "^1.9.4",
"@types/sortablejs": "^1.10.7",
"@typescript-eslint/eslint-plugin": "^5.20.0",
"@typescript-eslint/parser": "^5.20.0",
"@vitejs/plugin-legacy": "^1.8.1",
"@vitejs/plugin-vue": "^2.3.1",
"@vitejs/plugin-vue-jsx": "^1.3.10",
"@vue/compiler-sfc": "^3.2.33",
"@vue/test-utils": "^2.0.0-rc.18",
"@vue/vue3-jest": "^28.0.0",
"autoprefixer": "^10.4.4",
"commitizen": "^4.2.4",
"conventional-changelog-cli": "^2.2.2",
"cross-env": "^7.0.3",
"dotenv": "^16.0.0",
"eslint": "^8.13.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.6.0",
"esno": "^0.14.1",
"fs-extra": "^10.1.0",
"husky": "^7.0.4",
"inquirer": "^8.2.2",
"jest": "^28.1.0",
"jest-environment-jsdom": "^28.1.0",
"jest-environment-jsdom-global": "^3.1.2",
"jest-serializer-vue": "^2.0.2",
"less": "^4.1.2",
"lint-staged": "12.3.7",
"npm-run-all": "^4.1.5",
"picocolors": "^1.0.0",
"postcss": "^8.4.12",
"postcss-html": "^1.4.1",
"postcss-less": "^6.0.0",
"prettier": "^2.6.2",
"rimraf": "^3.0.2",
"rollup": "^2.70.2",
"rollup-plugin-visualizer": "^5.6.0",
"stylelint": "^14.7.1",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-recommended": "^7.0.0",
"stylelint-config-recommended-vue": "^1.4.0",
"stylelint-config-standard": "^25.0.0",
"stylelint-order": "^5.0.0",
"ts-jest": "^28.0.3",
"ts-node": "^10.7.0",
"typescript": "^4.6.3",
"vite": "^2.9.5",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-html": "^3.2.0",
"vite-plugin-imagemin": "^0.6.1",
"vite-plugin-mkcert": "^1.6.0",
"vite-plugin-mock": "^2.9.6",
"vite-plugin-purge-icons": "^0.8.1",
"vite-plugin-pwa": "^0.11.13",
"vite-plugin-style-import": "^2.0.0",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-theme": "^0.8.6",
"vite-plugin-vue-setup-extend": "^0.4.0",
"vite-plugin-windicss": "^1.8.4",
"vue-eslint-parser": "^8.3.0",
"vue-tsc": "^0.33.9"
},
"resolutions": {
"bin-wrapper": "npm:bin-wrapper-china",
"rollup": "^2.56.3",
"gifsicle": "5.2.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/anncwb/vue-vben-admin.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/anncwb/vue-vben-admin/issues"
},
"homepage": "https://github.com/anncwb/vue-vben-admin",
"engines": {
"node": "^12 || >=14"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [
"prettier --write--parser json"
],
"package.json": [
"prettier --write"
],
"*.vue": [
"eslint --fix",
"prettier --write",
"stylelint --fix"
],
"*.{scss,less,styl,html}": [
"stylelint --fix",
"prettier --write"
],
"*.md": [
"prettier --write"
]
}
}
2.配置
1)新建jest.config.mjs
export default {
preset: 'ts-jest',
roots: ['<rootDir>/tests/'],
clearMocks: true,
moduleDirectories: ['node_modules', 'src'],
moduleFileExtensions: ['js', 'ts', 'vue', 'tsx', 'jsx', 'json', 'node'],
modulePaths: ['<rootDir>/src', '<rootDir>/node_modules'],
testMatch: ['**/tests/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)', '(/__tests__/.*|(\\.|/)(test|spec))\\.(js|ts)$'],
testPathIgnorePatterns: ['<rootDir>/tests/server/', '<rootDir>/tests/__mocks__/', '/node_modules/'],
transform: {
'^.+\\.tsx?$': 'ts-jest',
'^.+\\.vue$': '@vue/vue3-jest',
},
transformIgnorePatterns: ['<rootDir>/tests/__mocks__/', '/node_modules/'],
// A map from regular expressions to module names that allow to stub out resources with a single module
moduleNameMapper: {
// '\\.(vs|fs|vert|frag|glsl|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/tests/__mocks__/fileMock.ts',
'\\.(sass|s?css|less)$': '<rootDir>/tests/__mocks__/styleMock.ts',
'\\?worker$': '<rootDir>/tests/__mocks__/workerMock.ts',
'^/@/(.*)$': '<rootDir>/src/$1',
},
snapshotSerializers: [
'jest-serializer-vue'
],
testEnvironment: 'jest-environment-jsdom-global',
verbose: true,
collectCoverage: false,
coverageDirectory: 'coverage',
collectCoverageFrom: ['src/**/*.{js,ts,vue}'],
coveragePathIgnorePatterns: ['^.+\\.d\\.ts$'],
};
2) tsconfig.json添加jest
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"forceConsistentCasingInFileNames": true,
"allowSyntheticDefaultImports": true,
"strictFunctionTypes": false,
"jsx": "preserve",
"baseUrl": ".",
"allowJs": true,
"sourceMap": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"experimentalDecorators": true,
"lib": ["dom", "esnext"],
"types": ["vite/client", "jest"],
"noImplicitAny": false,
"removeComments": true,
"paths": {
"/@/*": ["src/*"],
"/#/*": ["types/*"]
}
},
"include": [
"tests/**/*.ts",
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue",
"types/**/*.d.ts",
"types/**/*.ts",
"build/**/*.ts",
"build/**/*.d.ts",
"mock/**/*.ts",
"vite.config.ts"
, "src/views/Table/config.jsx" ],
"exclude": ["node_modules", "tests/server/**/*.ts", "dist", "**/*.js"]
}
3)写一个单例测试并运行查看
// test.spec.ts
import { mount } from '@vue/test-utils';
test('if jest is normal.', async () => {
expect('jest').toEqual('jest');
});
// The component to test
const MessageComponent = {
template: '<p>{{ msg }}</p>',
props: ['msg']
}
test('displays message', () => {
const wrapper = mount(MessageComponent, {
props: {
msg: 'Hello world'
}
})
// Assert the rendered text of the component
expect(wrapper.text()).toContain('Hello world')
})