Jest项目实战(2): 项目开发与测试

1. 项目初始化

首先,我们需要为开源库取一个名字,并确保该名字在 npm 上没有被占用。假设我们选择的名字是 jstoolpack,并且已经确认该名字在 npm 上不存在。

mkdir jstoolpack
cd jstoolpack
npm init -y

2. 安装依赖

接下来,我们需要安装一些开发和测试依赖。我们将使用 TypeScript 进行开发,并使用 Jest 进行单元测试。

"devDependencies": {"@types/jest": "^29.5.1","jest": "^29.5.0","jest-environment-jsdom": "^29.5.0","ts-jest": "^29.1.0","ts-node": "^10.9.1","typescript": "^5.0.4"
}
npm i @types/jest jest jest-environment-jsdom ts-jest ts-node typescript -D

3. 项目结构

在项目根目录下创建 src(源码目录)和 tests(测试目录)。项目本身不难,该项目是一个类似于 lodash 的工具库项目,会对常见的 array、function、string、object 等提供一些工具方法。

mkdir src tests

4. 配置 TypeScript

在项目根目录下创建 tsconfig.json 文件,配置 TypeScript 编译选项。

{"compilerOptions": {"target": "es6","module": "commonjs","strict": true,"esModuleInterop": true,"skipLibCheck": true,"forceConsistentCasingInFileNames": true,"outDir": "./dist","rootDir": "./src","baseUrl": ".","paths": {"*": ["node_modules/*"]}},"include": ["src/**/*.ts"],"exclude": ["node_modules", "dist"]
}

5. 配置 Jest

在项目根目录下创建 jest.config.js 文件,配置 Jest 测试框架。

module.exports = {preset: 'ts-jest',testEnvironment: 'jsdom',roots: ['<rootDir>/tests'],transform: {'^.+\\.tsx?$': 'ts-jest',},moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
};

6. 开发工具方法

6.1 range 方法

这里我们打算扩展一个名为 range 的方法,该方法可以生成指定范围的数组:

range(1, 6) ---> [1, 2, 3, 4, 5] 左闭右开
range(1, 6, 2) ---> [1, 3, 5]
range(1, 6, -2) ---> [1, 3, 5]range(6, 1) ---> [6, 5, 4, 3, 2]
range(6, 1, -2) ---> [6, 4, 2]
range(6, 1, 2) ---> [6, 4, 2]

对应的源码如下:

// 理论上来讲,start,stop,step 都应该是 number 类型
// 但是我们的代码最终是打包为 js 给开发者使用
// 开发者可能会存在各种非常的调用 range() range('a','b','c')
// 因此我们这里打算从方法内部进行参数防御,从而提升我们代码的健壮性
export function range(start?: any, stop?: any, step?: any) {// 参数防御start = start ? (isNaN(+start) ? 0 : +start) : 0;stop = stop ? (isNaN(+stop) ? 0 : +stop) : 0;step = step ? (isNaN(+step) ? 0 : +step) : 1;// 保证 step 的正确if ((start < stop && step < 0) || (start > stop && step > 0)) {step = -step;}const arr: number[] = [];for (let i = start; start > stop ? i > stop : i < stop; i += step) {arr.push(i);}return arr;
}

对应的测试代码如下:

import { range } from "../src/array";test("正常的情况", () => {expect(range(1, 6)).toEqual([1, 2, 3, 4, 5]);expect(range(1, 6, 2)).toEqual([1, 3, 5]);expect(range(6, 1)).toEqual([6, 5, 4, 3, 2]);expect(range(6, 1, -2)).toEqual([6, 4, 2]);
});test("错误的情况", () => {expect(range()).toEqual([]);expect(range("a", "b", "c")).toEqual([]);
});test("测试只传入start", () => {// 相当于结束值默认为 0expect(range(2)).toEqual([2, 1]);expect(range(-2)).toEqual([-2, -1]);
});test("测试step", () => {expect(range(1, 6, -2)).toEqual([1, 3, 5]);expect(range(6, 1, 2)).toEqual([6, 4, 2]);
});

6.2 truncate 方法

这里我们打算提供了一个 truncate 的方法,有些时候字符串过长,那么我们需要进行一些截取

truncate("1231323423424", 5) ----> 12...
truncate("12345", 5) ----> 12345
truncate("1231323423424", 5, '-') ----> 1231-

对应的源码如下:

export function truncate(str?: any, len?: any, omission = "...") {// 内部来做参数防御str = String(str);omission = String(omission);len = len ? Math.round(len) : NaN;if (isNaN(len)) {return "";}if (str.length > len) {// 说明要开始截断str = str.slice(0, len - omission.length) + omission;}return str;
}

对应的测试代码如下:

import { truncate } from "../src/string";test("应该将字符串截取到指定长度", () => {expect(truncate("Hello World", 5)).toBe("He...");expect(truncate("Hello World", 10)).toBe("Hello W...");expect(truncate("Hello World", 11)).toBe("Hello World");expect(truncate("Hello World", 15)).toBe("Hello World");expect(truncate("1231323423424", 5)).toBe("12...");expect(truncate("12345", 5)).toBe("12345");expect(truncate("1231323423424", 5, "-")).toBe("1231-");
});test("如果长度参数不是一个数字,那么返回一个空字符串", () => {expect(truncate("Hello World", NaN)).toBe("");expect(truncate("Hello World", "abc" as any)).toBe("");
});test("应该正确处理空字符串和未定义的输入", () => {expect(truncate("", 5)).toBe("");expect(truncate(undefined, 5)).toBe("un...");
});test("应该正确处理省略号参数", () => {expect(truncate("Hello World", 5, "...")).toBe("He...");expect(truncate("Hello World", 10, "---")).toBe("Hello W---");
});test("始终应该返回一个字符串", () => {expect(typeof truncate("Hello World", 5)).toBe("string");expect(typeof truncate("Hello World", NaN)).toBe("string");expect(typeof truncate(undefined, 5)).toBe("string");
});

6.3 debounce 方法

函数防抖是一个很常见的需求,我们扩展一个 debounce 方法,可以对传入的函数做防抖处理

对应的代码如下:

type FuncType = (...args: any[]) => any;
export function debounce<T extends FuncType>(func: T,wait: number
): (...args: Parameters<T>) => void {let timerId: ReturnType<typeof setTimeout> | null = null;return function (...args: Parameters<T>): void {if (timerId) {clearTimeout(timerId);}timerId = setTimeout(() => {func(...args);}, wait);};
}

对应的测试代码如下:

import { debounce } from "../src/function";beforeEach(() => {jest.useFakeTimers();
});afterEach(() => {jest.clearAllTimers();jest.useRealTimers();
});test("应该在等待时间之后调用函数",()=>{const func = jest.fn();const debouncedFunc = debounce(func, 1000);debouncedFunc();jest.advanceTimersByTime(500);expect(func).toHaveBeenCalledTimes(0);jest.advanceTimersByTime(500);expect(func).toHaveBeenCalledTimes(1);
})test("当防抖函数执行的时候,始终只执行最后一次的调用",()=>{const func = jest.fn();const debouncedFunc = debounce(func, 1000);debouncedFunc('a');debouncedFunc('b');debouncedFunc('c');jest.advanceTimersByTime(1000);expect(func).toHaveBeenCalledWith('c');
})test("在等待时间内又调用了函数,重置计时器",()=>{const func = jest.fn();const debouncedFunc = debounce(func, 1000);debouncedFunc();jest.advanceTimersByTime(500);debouncedFunc();jest.advanceTimersByTime(500);expect(func).toHaveBeenCalledTimes(0);jest.advanceTimersByTime(1000);expect(func).toHaveBeenCalledTimes(1);
})

7. 运行测试

package.json 中添加一个测试脚本。

{"scripts": {"test": "jest"}
}

运行测试命令:

npm test

8. 构建和发布

package.json 中添加构建脚本。

{"scripts": {"build": "tsc","test": "jest"}
}

构建项目:

npm run build

发布到 npm:

npm login
npm publish

总结

通过以上步骤,我们成功地搭建了一个简单的 JavaScript 工具库项目 jstoolpack,并实现了 rangetruncatedebounce 三个常用工具方法。我们使用了 TypeScript 进行类型检查,并使用 Jest 进行单元测试,确保代码的健壮性和可靠性。最后,我们通过 npm 发布了这个工具库,使其可以被其他开发者使用。

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

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

相关文章

windows运行ffmpeg的脚本报错:av_ts2str、av_ts2timestr、av_err2str => E0029 C4576

问题描述 我目前的环境是&#xff1a; 编辑器&#xff1a; Microsoft Visual Studio Community 2022 (64 位) 运行的脚本是ffmpeg自带的remux样例&#xff0c;只不过我想用c语言执行这个样例。在执行的过程中报错如下图&#xff1a; C4576 后跟初始值设定项列表的带圆括…

翻译工具开发技术笔记:《老挝语翻译通》app支持语音识别翻译功能,怎么提高语音识别的准确度呢?

《老挝语翻译通》app是一款专为老挝语翻译设计的免费工具&#xff0c;支持文本翻译、老挝文OCR文字识别提取、文字转语音。这款工具以其技术优势和用户友好的界面&#xff0c;为用户提供了便捷的老挝语翻译体验。 技术特点 文本翻译&#xff1a;支持双语输入&#xff0c;提供精…

Linux系统每日定时备份mysql数据

一、创建存储脚本的文件夹 创建文件夹&#xff0c;我的脚本放在/root/dbback/mysql mkdir ... cd /root/dbback/mysql 二、编写脚本 vi backup_mysql.sh 复制脚本内容 DB_USER"填写用户名" DB_PASSWORD"填写密码" DB_NAME"数据库名称" # …

MySQL基础-单表查询

语法 select [distinct] 列名1&#xff0c;列名2 as 别名... from数据表名 where组前筛选 group by分组字段 having组后筛选 order by排序的列 [asc | desc] limit 起始索引&#xff0c;数据条数 测试数据 # 建测试表 create table products (id int primary key a…

【Linux】Linux管道揭秘:匿名管道如何连接进程世界

&#x1f308;个人主页&#xff1a;Yui_ &#x1f308;Linux专栏&#xff1a;Linux &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;数据结构专栏&#xff1a;数据结构 &#x1f308;C专栏&#xff1a;C 文章目录 1.什么是管道 &#xff1f;2. 管道的类型2.1 匿…

前端学习Day13 CSS盒子的定位(固定定位篇“附练习”)

一、固定定位 固定定位 &#xff08;position:fixed&#xff09;其实是绝对定位的子类别&#xff0c;一个设置了 position:fixed 的元素是相对于视窗固定的&#xff0c;就算页面文档发生了滚动&#xff0c;它也会一直待在相同的地方。 ⚠️&#xff1a;固定定位会脱离文档流。…

Linux云计算 |【第五阶段】CLOUD-DAY9

主要内容&#xff1a; Metrics资源利用率监控、存储卷管理&#xff08;临时卷ConfitMap、EmptyDir、持久卷HostPath、NFS(PV/PVC)&#xff09; 一、Metrics介绍 metrics是一个监控系统资源使用的插件&#xff0c;可以监控Node节点上的CPU、内存的使用率&#xff0c;或Pod对资…

BM25:最佳匹配 ,文本相关性评分算法

目录 BM25:最佳匹配 一、BM25算法原理 二、BM25算法的应用场景 三、BM25算法的举例说明 BM25:最佳匹配 BM25(Best Matching 25)是一种在信息检索领域中广泛使用的文本相关性评分算法。它基于概率模型,考虑了词频(Term Frequency,TF)、逆文档频率(Inverse Documen…

论文阅读笔记:Image Processing GNN: Breaking Rigidity in Super-Resolution

论文阅读笔记&#xff1a;Image Processing GNN: Breaking Rigidity in Super-Resolution 1 背景2 创新点3 方法4 模块4.1 以往SR模型的刚性4.2 图构建4.2.1 度灵活性4.2.2 像素节点灵活性4.2.3 空间灵活性 4.3 图聚合4.4 多尺度图聚合模块MGB4.5 图聚合层GAL 5 效果5.1 和SOTA…

【游戏引擎之路】登神长阶(十二)——DirectX11教程:If you‘re going through hell, keep going!

【游戏引擎之路】登神长阶&#xff08;十二&#xff09;——DirectX11教程&#xff1a;If youre going through hell, keep going! 2024年 5月20日-6月4日&#xff1a;攻克2D物理引擎。 2024年 6月4日-6月13日&#xff1a;攻克《3D数学基础》。 2024年 6月13日-6月20日&#x…

编写虚拟的GPIO控制器的驱动程序:和pinctrl的交互使用

往期内容 本专栏往期内容&#xff1a; Pinctrl子系统和其主要结构体引入Pinctrl子系统pinctrl_desc结构体进一步介绍Pinctrl子系统中client端设备树相关数据结构介绍和解析inctrl子系统中Pincontroller构造过程驱动分析&#xff1a;imx_pinctrl_soc_info结构体Pinctrl子系统中c…

toolkit二次开发学习之程序集(ProAsmcomp)和装配体组件路径对象(ProAsmcomppath)

程序集ProAsmcomp可以理解为装配体组件对象。 对象ProAssembly是ProSolid的一个实例&#xff0c;并共享相同的声明。因此&#xff0c;ProAssembly对象可以作为适用于装配体的任何ProSolid和ProMdl函数的输入。特别是&#xff0c;因为你可以使用函数ProSolidFeatVisit()来遍历特…

【启程Golang之旅】深入理解 Protocol Buffers 及其应用

如果你是 Go 语言的开发者&#xff0c;理解如何在 Go 中使用 Protobuf&#xff0c;将帮助你大幅提升数据传输的效率&#xff0c;并实现更高性能的系统设计。 本篇文章将深入探讨 Go 语言中使用 Protobuf 的基础知识、常见应用以及最佳实践&#xff0c;带你一步步了解如何在项目…

Intent介绍#1

Intent 它是Android里非常重要的角色&#xff0c;基本上是核心人物。 能做的 startActivity --> 启动activitybroadcastIntent -> BroadcastContext.startService(Intent) Context.bindService(Intent, BindServiceFlags, Executor, ServiceConnection) -> Service…

鸿蒙开发案例:七巧板

【1】引言&#xff08;完整代码在最后面&#xff09; 本文介绍的拖动七巧板游戏是一个简单的益智游戏&#xff0c;用户可以通过拖动和旋转不同形状的七巧板块来完成拼图任务。整个游戏使用鸿蒙Next框架开发&#xff0c;利用其强大的UI构建能力和数据响应机制&#xff0c;实现了…

(61)使用LMS算法估计线性预测器并计算估计误差的MATLAB仿真

文章目录 前言一、仿真说明二、仿真代码三、仿真结果1.LMS自适应滤波器权向量更新曲线2.LMS自适应滤波器算法学习曲线3.期望信号与LMS自适应滤波器输出信号 前言 本文介绍了LMS自适应滤波器对线性预测器系统权系数的估计&#xff0c;进行100次独立实验&#xff0c;计算平均估计…

313页电力集团大数据应用支撑平台技术支撑服务项目技术投标方案

▲关注智慧方案文库&#xff0c;学习9000多份最新解决方案&#xff0c;其中 PPT、WORD超过7000多份 &#xff0c;覆盖智慧城市多数领域的深度知识社区&#xff0c;稳定更新4年&#xff0c;日积月累&#xff0c;更懂行业需求。 313页Word大型电力集团大数据应用支撑平台 技术支…

6款IntelliJ IDEA插件,让Spring和Java开发如虎添翼

文章目录 1、SonarLint2、JRebel for IntelliJ3、SwaggerHub插件4、Lombok插件5、RestfulTool插件6、 Json2Pojo插件7、结论 对于任何Spring Boot开发者来说&#xff0c;两个首要的目标是最大限度地提高工作效率和确保高质量代码。IntelliJ IDEA 是目前最广泛使用的集成开发环境…

标准IO的应用

1、使用这fscanf和fprintf两个函数实现文件的拷贝。 #include<myhead.h> int main(int argc, const char *argv[]) {FILE *fp;if((fpfopen("./1.txt","w"))NULL){perror("fopen");return -1;}int num100;printf("num%d\n",num…

CPU Study - Instructions Fetch

参考来源&#xff1a;《超标量处理器设计》—— 姚永斌 N-Way CPU 取指问题 如果CPU可以在每个周期内同时解码N条指令&#xff0c;则此类CPU为N-Way超标量处理器。 N-Way超标量处理器需要每个周期从I-Cache中至少取得N条指令&#xff0c;这N条指令成为一组Fetch Group。 为了…