vue3+vite+jest配置 单元测试总结

一、配置

从0接触单元测试,真的是安装环境,运行demo就折磨了好久,主要是各种版本库的依赖关系,所以安装的时候一定一定要注意安装对应的版本: 我的版本情况

  1. vue@3.2.2
  2. vite@2.5.0
  3. babel-jest@27
  4. jest@27
  5. ts-jest@27
  6. vue/vue3-jest@^28.1.0

vue-jest 一定要根据自己的版本情况选择合适的版本;

因为我的jest安装的是jest 27,所以一定要注意版本问题

css 复制代码
yarn add babel-jest@27 jest@27 ts-jest@27 -D
yarn add @babel/core @babel/preset-env babel-plugin-transform-es2015-modules-commonjs @vue/test-utils @vue/vue3-jest jest-transform-stub -D

每个库说明:

  • jest:提供单元测试能力。
  • ts-jest:Typescript 开发语言的预处理器
  • @vue/test-utils:对 Vue 组件进行测试(Vue 官方提供)。
  • @vue/vue3-jest:将 Vue SFC(单文件组件)转换为 Jest 可执行的 JavaScript 代码。
  • babel-jest:将非标准 JavaScript 代码(JSX/TSX)转换为 Jest 可执行的 JavaScript 代码
  • @babel/preset-env:提供测试时最新的 JavaScript 语法的 Babel Preset。
  • @babel/preset-typescript:提供测试时TypeScript 语法的 Babel Preset。
  • @vue/babel-plugin-jsx:提供测试时在 Vue 中使用 JSX/TSX 语法的 Babel Plugin。
  • @vitejs/plugin-vue-jsx:提供开发时在 Vue 中使用 JSX/TSX 语法的 Vite Plugin。
  • jest-transform-stub:将非 JavaScript 文件转换为 Jest 可执行的 JavaScript 代码。

设置配置jest.config.js:

css 复制代码
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: {
    '^.+\\.ts?$': 'ts-jest',
    '^.+\\.vue$': '@vue/vue3-jest',// 使用 vue-jest 帮助测试 .vue 文件
    '^.+\\.(js|jsx)?$': 'babel-jest',// 遇到 js jsx 等转成 es5
    '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',// 遇到 css 等转为字符串 不作测试
  },
  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',
  },
  testEnvironment: 'jsdom',
  verbose: true,
  collectCoverage: false,
  coverageDirectory: 'coverage',
  collectCoverageFrom: ['src/**/*.{js,ts,vue}'],
  coveragePathIgnorePatterns: ['^.+\\.d\\.ts$'],
};

设置配置babel.config.js

css 复制代码
module.exports = {
  presets: [
      [
          "@babel/preset-env",
          {
              targets: {
                  node: "current"
              }
          }
      ]
  ],
  plugins: ["transform-es2015-modules-commonjs"]
};

二、测试用例运行

1、vue组件测试用例

css 复制代码
//ComponentTest.vue
<template>
  <div class="bar">
    <h1>{{ count }}</h1>
    
    <h2 class="msg">{{ msg }}</h2>
    
    <h2 class="name">{{ props.name }}</h2>
    <button @click="handle">CLick</button>
  </div>
</template>

<script lang="ts" setup>
import { ref } from 'vue'
const props = defineProps({
  name:{
    type:String,
    default:'name'
  }
})
let count = ref<number>(0)
let msg = ref<string>('hello')
const handle = () => {
  count.value++
}
</script>

测试文件

css 复制代码
//com.spec.js
import { mount } from '@vue/test-utils';
import Component from '/@/views/home/ComponentTest.vue';

describe('Component', () => {
	test('is a Vue instance', () => {
		const wrapper = mount(Component, {
			props: {
				name: 'myName',
			},
		});
		// expect(wrapper.classes()).toContain('bar')
		expect(wrapper.vm.count).toBe(0);
		const button = wrapper.find('button');
		button.trigger('click');
		expect(wrapper.vm.count).toBe(1);

		expect(wrapper.find('.msg').text()).toBe('hello');

		expect(wrapper.find('.name').text()).toBe('myName');
		wrapper.unmount();
	});
});

2、ts代码测试用例

css 复制代码
//utils/index.js
// 是否手机号隐藏显示
export const handleTelReg=(tel:string,ishow:boolean) =>{
  let reg=/(\d{3})\d{4}(\d{4})/;
  if (ishow) {
    return tel
  }else{
    return tel.replace(reg,'$1****$2');
  }
}

// 数字千位显示123456=》123,456
export const formatNumber=(value: string) =>{
  value += '';
  const list = value.split('.');
  const prefix = list[0].charAt(0) === '-' ? '-' : '';
  let num = prefix ? list[0].slice(1) : list[0];
  let result = '';

  while (num.length > 3) {
    result = `,${num.slice(-3)}${result}`;
    num = num.slice(0, num.length - 3);
  }

  if (num) {
    result = num + result;
  }

  return `${prefix}${result}${list[1] ? `.${list[1]}` : ''}`;
}

测试文件

css 复制代码
import {formatNumber,handleTelReg} from '/@/utils/index'
test('格式化数字99999显示为99,999', () => {
  expect(formatNumber('99999')).toBe('99,999');
});
test('手机号隐藏显示为157****2026', () => {
  expect(handleTelReg('15755592026',false)).toBe('157****2026');
});

终端命令行运行jest 终端显示结果: 则单元测试用例成功了~~~~~

三、问题总结

问题1

css 复制代码
Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.


Details:

/home/xueyou/workspace/projects/node_modules/lodash-es/lodash.js:10

import * as lodash from 'lodash-es'

SyntaxError: Unexpected token *

原因 :项目使用jest进行测试时, 当引入外部库是es模块时, jest无法处理导致报错. 解决方案

  1. 安装依赖
css 复制代码
yarn add --dev babel-jest @babel/core @babel/preset-env babel-plugin-transform-es2015-modules-commonjs
  1. 配置babel.config.js
css 复制代码
module.exports = {
    presets: [
        [
            "@babel/preset-env",
            {
                targets: {
                    node: "current"
                }
            }
        ]
    ],
    plugins: ["transform-es2015-modules-commonjs"]
};
  1. 配置jest.config.js
css 复制代码
module.exports = {
    preset: "ts-jest",
    testMatch: ["<rootDir>/tests/**/*.(spec|test).ts?(x)"],
    transform: {
        // 将.js后缀的文件使用babel-jest处理
        "^.+\\.js$": "babel-jest",
        "^.+\\.(ts|tsx)$": "ts-jest"
    },
};

问题2

css 复制代码
Test suite failed to run

TypeError: Cannot destructure property 'config' of 'undefined' as it is undefined.

  at Object.getCacheKey (node_modules/vue-jest/lib/index.js:10:7)
  at ScriptTransformer._getCacheKey (node_modules/@jest/transform/build/ScriptTransformer.js:280:41)
  at ScriptTransformer._getFileCachePath (node_modules/@jest/transform/build/ScriptTransformer.js:351:27)
  at ScriptTransformer.transformSource (node_modules/@jest/transform/build/ScriptTransformer.js:588:32)
  at ScriptTransformer._transformAndBuildScript (node_modules/@jest/transform/build/ScriptTransformer.js:758:40)
  at ScriptTransformer.transform (node_modules/@jest/transform/build/ScriptTransformer.js:815:19)

原因:应该是插件版本冲突.比如你的jest,ts-jest等版本与你的vue-jest等版本有冲突 解决方案:可以参考以下问题 github.com/vuejs/vue-j... github.com/vuejs/test-... 或者参考我的插件版本

问题3

css 复制代码
● Test suite failed to run

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:
        SyntaxError: Unexpected token 'export'

      1 | import { withInstall } from '/@/utils';
    > 2 | import './src/index.less';
        | ^

原因:less文件引用有问题 解决方案: 在jest.config.js中配置moduleNameMapper

css 复制代码
  moduleNameMapper: {

    '\\.(sass|s?css|less)$': '<rootDir>/tests/__mocks__/styleMock.ts',

  },

然后在tests/mocks/styleMock.ts中增加代码module.exports = {};

详细可以查看文章juejin.cn/post/691650...

问题4

css 复制代码
Failed to write coverage reports:
        ERROR: Error: ENOENT: no such file or directory, scandir 'D:\huolalaFiles\comProjects\HeartRateSys\CareMoreWeb\node_modules\@jest\reporters\node_modules\istanbul-reports\lib\html\assets'
        STACK: Error: ENOENT: no such file or directory, scandir 'D:\huolalaFiles\comProjects\HeartRateSys\CareMoreWeb\node_modules\@jest\reporters\node_modules\istanbul-reports\lib\html\assets'
    at Object.readdirSync (fs.js:1043:3)
    at D:\huolalaFiles\comProjects\HeartRateSys\CareMoreWeb\node_modules\@jest\reporters\build\CoverageReporter.js:253:12
    at Array.forEach (<anonymous>)   

运行jest --coverage,一直报错,测试报告出不来,一直都说我的路径下缺少东西,找了很久看到一个类似问题的,先给我的解决方案叭 第一步:cd node_modules/@jest/reporters 第二步:yarn install 再次运行jest --coverage就可以成功的生成我的报告了 参考案例,仅供参考解决思路

问题5

css 复制代码
    Details:

    D:\huolalaFiles\comProjects\HeartRateSys\CareMoreWeb\src\hooks\web\useMessage.tsx:10
            return <icons_vue_1.InfoCircleFilled class="modal-icon-warning"/>;
                   ^
    SyntaxError: Unexpected token '<'

文件路径不对,但是在jest中,大概率是你的引用等没用处理好,可以对某个插件或者方法采用mock,将这个方法中间阻断即可 解决方案:

我写的主要是配置阶段出现的问题,最后推荐一个实战指导写的不粗的文章,可以看一下juejin.cn/post/703078...

相关推荐
esmap4 分钟前
OpenClaw与ESMAP AOA定位系统融合技术分析
前端·人工智能·计算机视觉·3d·ai·js
毕设源码-钟学长12 分钟前
【开题答辩全过程】以 基于node.js vue的点餐系统的设计与实现为例,包含答辩的问题和答案
前端·vue.js·node.js
小白路过15 分钟前
记录vue-cli-service serve启动本地服务卡住问题
前端·javascript·vue.js
We་ct19 分钟前
LeetCode 1. 两数之和:两种高效解法(双指针 + Map)
前端·算法·leetcode·typescript·哈希算法
LYFlied24 分钟前
边缘智能:下一代前端体验的技术基石
前端·人工智能·ai·大模型
1024小神25 分钟前
用css的clip-path裁剪不规则形状的图片展示
前端·css
铅笔侠_小龙虾30 分钟前
Flutter 组件层级关系
前端·flutter·servlet
梵得儿SHI33 分钟前
Vue 高级特性:渲染函数与 JSX 精讲(h 函数语法、JSX 在 Vue 中的应用)
前端·javascript·vue.js·jsx·模板语法·渲染函数·底层视图生成机制
GGGG寄了35 分钟前
CSS——文字控制属性
前端·javascript·css·html
菜鸟茜40 分钟前
ES6核心知识解析01:什么是ES6以及为什么需要ES6
前端·javascript·es6