NutUI 单元测试:从 jest 到 vitest

news2024/9/28 14:32:36

NutUI Vue 自 3.0 版本起,开始使用 vite 作为项目的构建工具,单元测试工具则依然使用 jest。而后 vite 官方团队开源了 vitest 作为 vite 的首选测试框架和 jest 的替代品。

本文主要介绍 NutUI 从 jest 到 vitest 的迁移过程,以及后续对于单元测试流程的改造升级。

一、时间线

  • 1、2023.04,NutUI 从 jest 迁移到 vitest,此时使用初期的 0.29.8 版本
    • 总计480 组单元测试
    • 迁移前后,单测运行总时间从 120s 下降为 48s
  • 2、2023.12,vitest 正式发布了 1.0.0 版本
  • 3、2024.01,测试环境从 jsdom 迁移到 happy-dom
    • 迁移前后,单测运行总时间从 40s 下降为 30s

二、从 jest 到 vitest 的流程

NutUI 仓库从 jest 到 vitest 的迁移源码参考:jdf2e/nutui#2217

下面将依次介绍完整的迁移流程:

1、包依赖变更

jest 默认仅支持处理 JS 文件,需要使用 ts-jest 插件或者使用 babel 预处理以支持 TS,对于 vue 组件则需要使用 vue-jest 插件做额外的转换。而这些对于 vitest 来说都不需要,它将这些复杂的流程交给 vite 处理。

jest 与 vitest 内部默认均使用 jsdom,它可以支持 canvas 的相关接口,需要依赖 canvas 包。很不幸,这个 node-canvas 包的安装过程就和 node-sass 一样痛苦。我们选择 jest-canvas-mock 以及基于它封装的 vitest-canvas-mock 插件做替代,它们的依赖更干净。

jest 插件vitest 插件备注
jest替换为 vitest
ts-jest移除
vue-jest移除
@types/jest移除
jest-canvas-mock替换为 vitest-canvas-mock
新增 @vitest/uivitest 的 UI 界面
新增 @vitest-coverage-c8vitest 测试覆盖率工具
2、配置修改

jest 的配置文件:

 

// jest.config.cjs

module.exports = {

moduleFileExtensions: ['vue', 'js', 'ts', 'tsx'],

preset: 'ts-jest',

testEnvironment: 'jsdom', // 测试环境

// 配置不同文件的处理逻辑

transform: {

'^.+\\.vue$': 'vue-jest', // vue 文件用 vue-jest 转换

'^.+\\.ts$': 'ts-jest' // ts 文件用 ts-jest 转换

},

// 匹配 __tests__ 目录下的 .js/.ts 文件 或其他目录下的 xx.test.js/ts xx.spec.js/ts

testRegex: '(/__tests__/*|(\\.|/)(test|spec))\\.(ts|tsx)$',

//testRegex: '__tests__.action.spec.ts',

// alias 配置

moduleNameMapper: {

'^@/(.*)$': '<rootDir>/src/$1'

},

// 是否开启将测试覆盖率信息输出为报告

collectCoverage: true,

// 覆盖率报告涉及哪些文件

collectCoverageFrom: [

'src/packages/__VUE/**/*.{js,jsx,ts,tsx,vue}',

'!**/node_modules/**',

'!**/demo.vue/**',

'!**/index.taro.vue/**'

]

};

vitest 的配置则更简洁一些,因为它可以复用 vite 的配置项,包括但不限于 alias、插件系统、css 处理逻辑等等。

只需要在已有的 vite.config.ts 文件中做以下调整:

 

/// <reference types="vitest" />

// 增加上面这行注释标记后,项目中的 vitest 将以当前文件作为配置文件


export default defineConfig({

// 这里是原项目中的 vite 配置

...

// 下面是新增的 vitest 的配置

test: {

globals: true, // 是否开启 vitest 的全局变量注入

environment: 'jsdom', // 测试环境,默认 jsdom,可选项 happy-dom

coverage: {

all: false, // 覆盖率报告是否以所有文件作为基准。不开启时,会将所有测试用例关联到的文件作为基准。

provider: 'c8' // 测试覆盖率工具。在 vitest 的后续版本中将升级为 v8

},

// 匹配所有的测试用例文件

include: ['src/packages/__VUE/**/*.(test|spec).(ts|tsx)'],

// 输出测试报告的格式

reporters: ['default', 'html']

}

});
3、测试用例

vitest 的 API 设计几乎保持了与 jest 一致,测试用例的修改步骤很简单,只需要一些批量替换操作,就可以运行起来。

  • 全局变量 jest 需要批量替换为 vi
    • jest.mock --> vi.mock
    • jest.fn --> vi.fn
  • 测试快照:运行 vitest 更新所有快照文件
    • 快照文件的签名从 jest 变为 vitest
    • 组件快照变得更加简洁直观,不再包含多余的转义符 参考这个 PR

经过以上三个步骤,就已经完成了从 jest 到 vitest 的迁移。

4、迁移效果

vitest 具有以下优势:

  • 1、速度更快:整个测试流程从 120s 下降到了 48s
  • 2、UI 界面:通过 @vitest/ui 可以在单测运行时启动一个网页,展示所有的测试用例运行情况、模块之间的关联图,搭配 vscode 插件后可以在源码中标出具体出错的代码行。
  • 3、HMR:从未有过的体验,单元测试也可以做到热更新。
  • 4、原生支持 ESM、TS 等等:jest 似乎至今仍未默认支持 ESM 模块,而大多数框架已经开始逐渐淘汰 CJS 了。

对于一个 vite 项目,使用 vitest 的配置会更加简单,本质原因在于它与 Vite 使用了一致的配置、转换器、解析器和插件。对于代码的转换流程,可以直接使用项目中已有的 vite 插件进行处理。而在 vite 项目中使用 jest 就相当于对同一个处理逻辑配置了两套完全不同的处理逻辑,配置繁琐,运行效率低。

三、迁移后的建设

在迁移到 vitest 之后,我们对测试流程又做了一些改进工作。

1、接入 codecov

在每一次 PR/Push 时,执行 vitest 测试流程,输出单测覆盖率报告并上传至 codecov 网站,这里可以看到 NutUI 项目单测覆盖率的完整数据:codecov/jdf2e/nutui

2、使用 TSX 编写测试用例

在以往使用 jest + vue-test-utils 时,编写 vue 组件的测试用例通常会使用以下两种方式:

方式一:渲染函数

// test.ts

import { mount } from '@vue/test-utils';

import Button from '../index.vue';


test('Button', async () => {

const wrapper = mount(Button, {

props: {},

slots: {},

// ...

});

const btn = wrapper.find('.nut-button');

// 断言 ...

});

在 vue 中使用渲染函数编写组件是一件很痛苦的事情,对于大量使用插槽的复杂组件尤其麻烦。这种方式只适合编写简单组件的测试用例。

方式二:模板语法
 

// test.ts

import { mount } from '@vue/test-utils';

import { ref } from 'vue';

import Button from '../index.vue';


test('Button', async () => {

const wrapper = mount({

template: `

<template>

<nut-button @click="onClick"> {{ count }} </nut-button>

</template>

`,

setup() {

const count = ref(0);

const onClick = () => {

count.value++;

};

return { onClick };

}

});

// 断言 ...

});

这种方式用纯字符串的方式编写 vue 模板,不再需要繁杂的渲染函数,整体代码风格为选项式 defineComponent + setup 函数的形式。

它存在一个更严重的问题,vue 模板丢失了 TS 类型校验,并且选项式 setup 的风格依然存在大量冗余代码。

方式三:TSX

在切换到 vitest 之后,我们很容易获得了第三种方式。只需要在 vite 配置中增加 @vitejs/plugin-vue-jsx 即可。

想象一下在 jest 框架下,如何支持 tsx 格式的单测用例呢?

vue-jest 并不支持,我们可能需要一个 vue-jsx-jest 插件(它并不存在)去做这层转换,或者手动引入 @vue/babel-plugin-jsx 插件进行处理。

 

// test.tsx

import { mount } from '@vue/test-utils';

import { ref } from 'vue';

import Button from '../index.vue';


test('Button', async () => {

const count = ref(0);

const onClick = () => {

count.value++;

};

const wrapper = mount(() => {

return (

<Button onClick={onClick}> {{ count }} </Button>

);

});

// 断言 ...

});

虽然 vue 主推的代码风格为 SFC + script setup,但是单元测试本身需要使用 js/ts 编写,如果引入外部的 .vue 文件也会变得繁琐。笔者认为 tsx 是当前最合适的 vue 组件单测用例代码风格。

3、测试环境从 jsdom 到 happy-dom

迁移源码参考:jdf2e/nutui#2881

jest 和 vitest 的默认测试环境均为 jsdom,它是一个比较成熟的、主流的方案,而 happy-dom 是一个新的挑战者。它迭代更频繁,对于新特性的支持更迅速。

计划从 jsdom 切换到 happy-dom 的念头来自这样一个简单的例子:

某个组件中使用了 css 样式 background-origin: border-box,在 jest 测试中不能被识别。原因在于 jsdom 使用 jsdom/cssstyle 进行 css 相关的处理,这里并没有发现这个样式的识别逻辑。

从 jsdom 切换到 happy-dom 之后,主要产生了以下影响:

  • 1、对于颜色的处理逻辑,jsdom 会转换为 16 进制颜色,而 happy-dom 则保持原始值
  • 2、happy-dom 可识别的 HTML、CSS 属性更丰富
  • 3、单测快照文件与行覆盖率未产生明显变化(<1%)
  • 4、运行速度进一步加快,单测 CI 流程时间从 40s 下降到 30s

四、最后

感谢阅读,文中可能存在不准确或错误之处,如果您发现任何问题,欢迎指正,非常感激。

NutUI GitHub 仓库:

  • Vue:jdf2e/nutui
  • React:jdf2e/nutui-react

NutUI 官网:nutui.jd.com

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:【文末自行领取】

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2173801.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电商系统开发全攻略:基于Spring Boot的在线商城

2 相关技术 2.1 Springboot框架介绍 Spring Boot是由Pivotal团队提供的全新框架&#xff0c;其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置&#xff0c;从而使开发人员不再需要定义样板化的配置。通过这种方式&#xff0c;Spring…

Go 项目开发常用设计模式

设计模式就某些编码场景下的最佳实践&#xff0c;用来解决常见的软件设计问题。Go 语言不是面向对象语言&#xff0c;但是可以使用结构体、接口等类型实现面向对象语言的特性&#xff0c;想要弄懂设计模式&#xff0c;要熟练的使用 Go 语言接口类型 和结构体类型 设计模式总体上…

关于Chrome浏览器F12调试,显示未连接到互联网的问题

情况说明 最近笔者更新下电脑的Chrome浏览器&#xff0c;在调试前端代码的时候&#xff0c;遇到下面一个情况&#xff1a; 发现打开调试面板后&#xff0c;页面上显示未连接到互联网&#xff0c;但实际电脑网络是没有问题的&#xff0c;关闭调试面板后&#xff0c;网页又能正…

Python 爬虫 根据ID获得UP视频信息

思路&#xff1a; 用selenium库对网页进行获取&#xff0c;然后用bs4进行分析&#xff0c;拿到bv号&#xff0c;标题&#xff0c;封面&#xff0c;时长&#xff0c;播放量&#xff0c;发布时间 先启动webdriver.&#xff0c;进入网页之后&#xff0c;先等几秒&#xff0c;等加…

chatglm本地服务器大模型量化cpu INT4 INT8 half float运行、多卡多GPU运行改这一条指令就行啦!

一、ChatGLM3的几种推演方式 ChatGLM3常规方案的GPU推演中half和float是两种最常用的格式&#xff0c;half格式占13GB显存&#xff0c;float格式占40GB显存。此外还提供了几种GPU量化格式的推演&#xff1a;INT4和INT8量化。 CPU版本的ChatGLM3推演&#xff1a; model Auto…

Java 常用的一些Collection的实现类

Java 常用的一些Collection的实现类 Collection 1.集合基础 Java 集合框架是一个强大的工具&#xff0c;它提供了一套标准化的接口和类&#xff0c;用于存储和操作集合数据。Collection 接口是这个框架的核心&#xff0c;它定义了一系列通用的集合操作。 2.Collection接口方法 …

既然有HTTP协议,为什么还要有RPC?

既然有HTTP协议&#xff0c;为什么还要有RPC&#xff1f; ​ 既然有HTTP协议&#xff0c;为什么还要有RPC&#xff1f; 有点既生瑜何生亮的味道。 第一次接触RPC我就很懵&#xff0c;平时我HTTP协议用得好好的&#xff0c;为什么还需要RPC协议&#xff1f; 于是我去百度&am…

最详细!适合AI大模型零基础入门的学习路线+学习方法+学习资料,全篇干货,建议收藏!

前言 随着ChatGPT的横空出世&#xff0c;大模型时代正式来临。千亿甚至万亿参数的大模型陆续出现&#xff0c;各大企业、高校纷纷推出自己的大模型&#xff0c;这标志着通用智能时代的到来。对于零基础的初学者来说&#xff0c;如何快速入门AI大模型&#xff0c;抓住这个时代的…

通过管道和共享存储映射实现进程通信

1.IPC方法 Linux环境下&#xff0c;进程地址空间相互独立&#xff0c;每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到&#xff0c;所以进程和进程之间不能相互访问&#xff0c;要交换数据必须通过内核&#xff0c;在内核中开辟一块缓冲区&a…

物联网智能项目全面解析

目录 引言 一、物联网概述 1.1 什么是物联网 1.2 物联网的历史与发展 二、物联网智能项目分类 三、关键组件与技术 3.1 传感器和执行器 3.2 连接技术 3.3 数据处理与分析 3.4 用户界面 四、物联网智能项目案例分析 4.1 智能家居 4.2 智慧城市 4.3 工业物联网 4.4…

孩子英语不好,能学编程吗?

随着编程教育的普及&#xff0c;越来越多的家长开始关注孩子的编程学习。然而&#xff0c;不少家长提出了这样的疑问&#xff1a;“孩子的英语不好&#xff0c;是否还能学编程&#xff1f;”毕竟&#xff0c;编程语言是基于英语的&#xff0c;代码中也充斥着大量的英语单词和短…

数据结构-3.6.队列的链式实现

队列可以理解为单链表的阉割版&#xff0c;相比单链表而言&#xff0c;队列只有在添加和删除元素上和单链表有区别 一.队列的链式实现&#xff1a; 1.图解&#xff1a; 2.代码&#xff1a; #include<stdio.h> ​ typedef struct LinkNode //链式队列结点 {int data;st…

【韩顺平Java笔记】第1章

0-1可以看视频&#xff0c;下面记录主要内容 2. 就业方向 Java基础又叫JavaSE&#xff0c;Java有三个主要的就业方向&#xff1a; JavaEE软件工程师&#xff1a;电商&#xff0c;团购&#xff0c;众筹&#xff0c;sns&#xff08;社交网络&#xff09;&#xff0c;教育&…

纠删码参数自适应匹配问题ECP-AMP实验方案(一)

摘要 关键词&#xff1a;动态参数&#xff1b;多属性决策&#xff1b;critic权重法&#xff1b;DBSCA聚类分析 引言 云服务存储系统是一种基于互联网的数据存储服务&#xff0c;它可以为用户提供大规模、低成本、高可靠的数据存储空间。云服务存储系统的核心技术之一是数据容…

winsoft公司Utils组件功能简介

Winsoft Utils Library 2.3 是一个为 Delphi 和 C Builder&#xff08;版本 7 到 12 Athens&#xff09;设计的实用工具库。它提供了一系列组件和类&#xff0c;旨在简化和增强开发过程。以下是一些主要功能和特点&#xff1a; 1.组件集合&#xff1a;包含多种实用组件&#x…

AB plc设备数据 转profinet IO项目案例

目录 1 案例说明 1 2 VFBOX网关工作原理 1 3 准备工作 2 4 网关采集AB PLC数据 2 5 用PROFINET IO协议转发数据 4 6 案例总结 7 1 案例说明 设置网关采集AB PLC数据把采集的数据转成profinet IO协议转发给其他系统。 2 VFBOX网关工作原理 VFBOX网关是协议转换网关&#xff0…

如果再回到从前——备忘录模式

文章目录 如果再回到从前——备忘录模式如果再给我一次机会……游戏存进度备忘录模式备忘录模式基本代码游戏进度备忘 如果再回到从前——备忘录模式 如果再给我一次机会…… 时间&#xff1a;5月6日18点  地点&#xff1a;小菜、大鸟住所的客厅  人物&#xff1a;小菜、…

Sharding-JDBC笔记03-分库分表代码示例

文章目录 一、水平分库1. 将原有order_db库拆分为order_db_1、order_db_22. 分片规则修改分片策略standardcomplexinlinehintnone 3. 插入测试4. 查询测试5. 使用分库分片键查询测试总结 二、公共表1. 创建数据库2. 在Sharding-JDBC规则中修改3. 数据操作4. 字典操作测试5. 字典…

Linux线程-POSIX信号量与锁以及条件变量

POSIX信号量 POSIX没有元素这个概念相比于SYSTEM-V更简洁&#xff0c;POSIX不一定适用老版本&#xff1b;二者都是系统范畴&#xff0c;都需要手动删除&#xff0c;POSIX相关函数属于线程库&#xff0c;所有编译时需要末尾加上-lpthread选项 POSIX POSIX有名信号量 主要用于进…

华为 HCIP-Datacom H12-821 题库 (29)

&#x1f423;博客最下方微信公众号回复题库,领取题库和教学资源 &#x1f424;诚挚欢迎IT交流有兴趣的公众号回复交流群 &#x1f998;公众号会持续更新网络小知识&#x1f63c; 1.BFD 为确保两端系统都知道状态的变化&#xff0c;在BFD 状态机的建立和拆除时都采用三次握手…