文章目录

  • 前言
  • 一、直接创建一个继承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单测_前端


这里选择jest

jest 如何出现报表 jest单测_vue.js_02


项目创建好之后就可以看见这个项目已经具备jest

jest 如何出现报表 jest单测_vue.js_03

2. 运行查看测试结果

运行命令yarn test:unit可以看到运行成功

jest 如何出现报表 jest单测_jest 如何出现报表_04


当然也可以将它估计改成不匹配的来查看结果

二、vue2已有项目中手动配置

可能有的小伙伴手里已经有了项目,不允许重新创建,那么这个方法可能更适合你
官方文档

1. 安装jest和Vue Test Utils

因为单元测试只在dev环境使用所以按在dev下即可 两个包用空格隔开,可以同时安两个包()

npm install --save-dev jest @vue/test-utils

在 package.json添加命令

jest 如何出现报表 jest单测_jest 如何出现报表_05

2. 安装vue-jest处理单文件组件

运行命令

npm install --save-dev vue-jest

添加好配置

jest 如何出现报表 jest单测_前端_06

如果有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"
                }
            }
        ]
    ]
}

jest 如何出现报表 jest单测_前端_07

4. 安装babel-jest

npm install --save-dev babel-jest

jest 如何出现报表 jest单测_单元测试_08

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')
})

jest 如何出现报表 jest单测_json_09

二、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

jest 如何出现报表 jest单测_json_10

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

jest 如何出现报表 jest单测_前端_11

{
  "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)写一个单例测试并运行查看

jest 如何出现报表 jest单测_jest 如何出现报表_12

// 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')
})