Bootstrap

前端项目接入单元测试手册

一、单元测试

Vue.js项目中的单元测试是一种软件测试方法,通过对最小的、可测试的代码单元进行检查和验证来保证代码质量。确保每个组件作为独立单元正确执行其预定功能。当代码库随着时间发展增长时,单元测试成为识别错误和避免潜在问题的关键手段。此外,编写单元测试能驱动开发者写出更干净、有组织的代码。

二、实现单元测试需掌握以下几点:

1、熟悉Vue Test Utils库;
2、掌握测试运行器Jest的使用;
3、理解掌握测试覆盖率的重要性;

测试覆盖率是衡量测试完整性的一个重要指标,指示了代码被测试所覆盖的程度。完整的测试覆盖率能够让开发者放心地更改和重构代码。在Vue项目中,可以通过配置Jest的collectCoverage选项来生成覆盖率报告。高测试覆盖率有助于确保应用的稳定性,同时也是评估测试效果的一种方式。

4、学会模拟依赖和API调用;

在单元测试中,模拟外部依赖和API调用是不可或缺的一部分。模拟可以帮助我们创建一个可控的测试环境,确保组件的单元测试不会受到外部环境的影响。在Vue项目中,开发者可以使用Jest的mock功能来模拟依赖,包括模块、函数甚至是整个库。通过Jest的axios-mock-adapter插件,可以轻松模拟HTTP请求,从而测试组件对API调用的响应。

5、前端单元测试常会用到的几点:

组件渲染:测试组件是否正确渲染,包括检查模板中的元素、属性和内容。确保组件在不同属性和状态组合下都能正确显示。
事件处理:测试组件事件触发器的行为,包括点击、输入、鼠标悬停等。验证事件处理器中的函数是否被正确调用,并检查事件传递的参数。
数据和方法:测试组件的数据和方法,确保它们按预期工作。验证计算属性和侦听器是否根据依赖项的变化而更新。
Props验证:验证组件接受的props类型、必要性和默认值是否符合预期。确保传递无效props时组件能够正确处理或给出警告。
插槽内容:如果组件使用了插槽,测试插槽内容的渲染和行为。确保插槽内容能够正确接收和显示传递给它的数据。
边界条件和异常情况:测试组件在边界条件和异常情况下的表现,如空值、未定义变量或错误的数据类型。确保组件能够优雅地处理这些情况,而不是崩溃或产生不可预测的行为。
性能优化:对于性能敏感的部分,可以编写单元测试来确保代码优化得当。例如,测试组件的渲染速度、内存使用情况或是否避免了不必要的重渲染。
6、在编写单元测试时,建议遵循以下原则:

保持测试简洁:每个测试用例应专注于一个具体的功能点或行为。
使用描述性名称:为测试用例和测试函数使用描述性名称,以便于理解测试的目的和预期结果。
避免冗余测试:不要重复测试已经在其他测试用例中验证过的功能。
模拟外部依赖:对于涉及外部API调用或复杂依赖的组件,使用模拟数据或模拟函数来简化测试环境。
通过关注上述方面并遵循相关原则,可以有效地为Vue 2前端项目编写单元测试,提高代码质量和可维护性。

三、工具介绍

@vue/test-utils:这是Vue官方提供的用于组件单元测试的工具库。关键是能够在隔离的环境下测试组件的行为,而无需担心与其他组件或状态的交互。Vue Test Utils为Vue项目中的单元测试提供了一系列有用的API,它使得测试Vue组件变得简单,能模拟用户交互,如点击、输入并断言组件行为。使用Vue Test Utils进行单元测试意味着可测试组件模板、脚本和样式,从而确保每个组件按预期工作,并能稳健处理边界情况。版本2文档 @vue/test-utils 版本1文档 @vue/test-utils
Jest:jest是facebook推出的一款测试框架,集成了前面所讲的Mocha和chai,jsdom,sinon等功能,是一个令人愉悦的JavaScript测试框架,具有易于上手、快速反馈的特点,特别适用于大型项目。Jest的自动化测试能够极大提高开发效率并减少手动测试的工作。它自带测试覆盖率报告、模拟对象和定时器,支持快照测试。在Vue项目中,Jest可以与Vue Test Utils协同工作,提供一整套解决方案来执行单元测试。快速开始 · Jest

四、安装与配置

1、浏览器环境

npm install --save-dev jsdom jsdom-global

2、单元测试插件
需要接入的插件有jest、@vue/test-utils@^1.*

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

3、配置单元测试脚本
然后我们需要在 package.json 中定义一个单元测试的脚本。

// package.json
{
  "scripts": {
    "test": "jest"
  }
}

4、安装和配置 vue-jest 预处理器
为了告诉 Jest 如何处理 *.vue 文件,我们需要安装和配置 vue-jest 预处理器:

npm install --save-dev vue-jest

接下来在 package.json 中创建一个 jest 块:

{
  // ...
  "jest": {
    "moduleFileExtensions": [
      "js",
      "json",
      // 告诉 Jest 处理 `*.vue` 文件
      "vue"
    ],
    "transform": {
      // 用 `vue-jest` 处理 `*.vue` 文件
      ".*\\.(vue)$": "vue-jest"
    }
  }
}

5.为 Jest 配置 Babel
尽管最新版本的 Node 已经支持绝大多数的 ES2015 特性,你可能仍然想要在你的测试中使用 ES modules 语法和 stage-x 的特性。为此我们需要安装 babel-jest:

npm install --save-dev babel-jest babel-core@^7.0.0-bridge.0 @babel/core

接下来,我们需要在 package.json 的 jest.transform 里添加一个入口,来告诉 Jest 用 babel-jest 处理 JavaScript 测试文件:

{
  // ...
  "jest": {
    // ...
    "transform": {
      // ...
      // 用 `babel-jest` 处理 js
      "^.+\\.js$": "<rootDir>/node_modules/babel-jest"
    }
    // ...
  }
}

.babelrc 配置:

{
    "env": {
    "test": {
      "presets": [["env", { "targets": { "node": "current" } }]]
    }
  }
}

6、配置Jest支持TypeScript
需要在 Jest 中设置编译 TypeScript。需安装 ts-jest:

 npm install --save-dev ts-jest @types/jest

然后在 package.json 中设置使用 ts-jest 处理 TypeScript 测试文件:

// package.json
{
  // ...
  "jest": {
    "moduleFileExtensions": [
      "js",
      "ts",
      "tsx",
      "vue"
    ],
    "transform": {
      "^.+\.js$": "<rootDir>/node_modules/babel-jest",
      "^.+\.tsx?$": "ts-jest",
      ".*\.(vue)$": "vue-jest"
    }
  }
}

最后在tsconfig.json文件中声明jest

// tsconfig.json
{
  "compilerOptions": {
    //...
    "types": ["jest"]
  }
}

7.放置测试文件
默认情况下,Jest 将会递归的找到整个工程里所有 .spec.js 或 .test.js 扩展名的文件。如果这不符合你的需求,你也可以在 package.json 里的配置段落中改变它的 testRegex

{
  // ...
  "jest": {
    // ...
    "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
    // ...
  }
}

8.测试覆盖率
Jest 可以被用来生成多种格式的测试覆盖率报告。以下是一个简单的起步的例子:

扩展你的 jest 配置 (通常在 package.json 或 jest.config.js 中) 的 collectCoverage 选项,然后添加 collectCoverageFrom 数组来定义需要收集测试覆盖率信息的文件。

{
  "jest": {
    // ...
    "collectCoverage": true,
    "collectCoverageFrom": ["**/*.{js,vue}", "!**/node_modules/**"]
  }
}

这样就会开启默认格式的测试覆盖率报告。你可以通过 coverageReporters 选项来定制它们。

{
  "jest": {
    // ...
    "coverageReporters": ["html", "text-summary"]
  }
}

测试覆盖率参数:
Stmts 语句覆盖率
Branch 分支覆盖率
Funcs 函数覆盖率
Lines 行覆盖率

更多文档内容请移步至 Jest 配置文档,在那里你可以找到覆盖率阀值、目标输出目录等选项。

9.测试规范示例

import { mount } from '@vue/test-utils'
import Component from './component'

describe('Component', () => {
  test('is a Vue instance', () => {
    const wrapper = mount(Component)
    expect(wrapper.isVueInstance()).toBeTruthy()
  })
})

10.运行测试命令脚本

npm run test

11.其他
处理webpack别名
如果在 webpack 中配置了别名解析,比如把 @ 设置为 /src 的别名,在jest中也需要用 moduleNameMapper 选项配置

{
  // ...
  "jest": {
    // ...
    // 支持源代码中相同的 `@` -> `src` 别名
    "moduleNameMapper": {
      "^@/(.*)$": "<rootDir>/src/$1",
      "@types": "<rootDir>/src/types/index"
    }
  }
}

当前我用的包版本

"vue": "2.7.14",
"@babel/preset-typescript": "^7.26.0",
"@types/jest": "^28.1.8",
"babel-preset-next": "^1.4.0",
"ts-jest": "^29.2.5",
"typescript": "~3.9.3",

推荐jest相关配置包版本

"@vue/test-utils": "^1.3.6",
"jest": "^29.7.0",
"jest-sonar-reporter": "^2.0.0",
"jest-environment-jsdom": "^29.7.0",
"vue-jest": "^3.0.7",
"babel-jest": "^29.7.0",
"vue-template-compiler": "2.7.14", // 与vue版本对应
"babel-core": "^7.0.0-bridge.0",
"identity-obj-proxy": "^3.0.0",

问题梳理

1.jest 不支持解析scss,以下方案行不通。把scss变量不导出的方式解决
https://stackoverflow.com/questions/60841440/syntaxerror-for-scss-in-vue-jest
https://github.com/vuejs/vue-test-utils-jest-example/issues/11
https://github.com/routablehq/jest-scss-transform

参考示例

在UniApp中使用Vue Test Utils和Jest进行单元测试-百度开发者中心
记录使用vue-test-utils + jest 在uniapp中进行单元测试_uniapp 单元测试-CSDN博客
趣谈 Jest 配置

总结与展望

1.接入环境:vue2项目、乾坤框架下vue2项目
2.项目资源:element-ui、yxt-ui 资源引入
3.jest 不支持 scss 资源,如何处理
4.jest 语法有学习成本

;