如何从0构建一款类jest工具

 Jest工作原理

  Jest 是一个流行的 JavaScript 测试框架,特别适用于 React 项目,但它也可以用来测试任何 JavaScript 代码。Jest 能够执行用 JavaScript 编写的测试文件的原因在于其设计和内部工作原理。下面是 Jest 的工作原理及其内部机制的详细解释,大致分为6部分
初始化
Jest 在启动时会读取配置文件(如 jest.config.js)以及命令行参数来初始化自身的配置。
它会设置全局环境,包括全局变量、钩子函数(如 beforeEach、afterEach)等。
发现测试文件
Jest 会遍历指定的目录,使用配置文件中定义的模式(如 **/__tests__/**/*.js?(x) 或 **/?(*.)+(spec|test).js?(x))来发现测试文件。
这些文件通常是以 .test.js 或 .spec.js 结尾,或者位于 __tests__ 目录下。
编译和转换
对于使用现代 JavaScript 语法(如 ES6、ES7、JSX)的测试文件,Jest 会使用 Babel 或其他编译工具将其转换为兼容的 JavaScript 代码。Jest 内部集成了 Babel,能够自动识别并转换这些语法。
隔离环境执行
Jest 为每个测试文件创建一个独立的沙盒环境。这个环境隔离了全局变量和模块缓存,确保测试之间不会相互干扰。通过 jest-runtime 模块,Jest 能够在这个隔离环境中加载并执行测试文件。
执行测试
Jest 通过导入测试文件并执行其中的测试函数(如 test 或 it 函数)来运行测试。
Jest 会跟踪每个测试的结果,包括成功、失败、跳过等信息。
报告和反馈
测试执行完毕后,Jest 生成详细的测试报告,包括每个测试的结果、执行时间、失败的断言信息等。Jest 还支持代码覆盖率报告,帮助开发者了解测试覆盖的代码范围。

  这些模块中,隔离测试环境执行测试文件是很核心的模块,那么jest如何实现隔离环境以及执行测试的呢?实现这些的是jest-runtime模块,jest-runtime 通过 vm 模块(Node.js的虚拟机模块)在沙盒环境中执行测试文件。创建一个独立的执行上下文,并在其中运行测试代码,确保测试文件与主进程隔离。另外,jest-runtime 还提供了一组全局变量(如 test、expect、beforeEach、afterEach 等)供测试文件使用。这里使用到了vm模块,那什么是node.js的vm模块呢?

Node.js的vm模块

  Node.js 的 vm 模块允许在 V8 虚拟机的上下文中编译和运行代码。它提供了几种 API来创建独立的执行环境(沙盒),用于隔离代码执行。这在以下场景中非常有用:

执行不信任的代码:通过沙盒环境执行用户输入的代码,以防止代码影响到主应用程序。
插件系统:动态加载和执行插件代码。
测试:在隔离的环境中运行测试代码,防止全局状态污染。
动态代码生成:根据运行时数据动态生成并执行代码。

  利用vm编译和执行代码非常简单,具体代码如下图所示,当需要执行某段代码的时候,将code的字符串传入runInSandbox即可。

import { Script, createContext } from 'vm';
import assert from 'assert';
export const runInSandbox = (code, context = {}) => {const sandbox = createContext({...context,assert,console,setTimeout,setInterval,clearTimeout,clearInterval,Buffer,process,});const script = new Script(code);script.runInContext(sandbox);
};

  上面的的代码中,主要是用了vm模块提供Script对象和createContext方法。Script 对象是 vm 模块中的一个核心组件,它允许你编译并运行一段 JavaScript 代码。script.runInContext(context) 方法,可以在指定的上下文中运行编译后的代码。上下文是通过 vm.createContext 创建的。createContext中主要定义两种变量,全局变量和用户自定义变量。全局变量主要包括如下对象:
console: 提供日志记录功能,使沙盒内的代码能够输出日志信息。
setTimeout, setInterval, clearTimeout, clearInterval: 这些计时器函数允许沙盒内的代码使用异步操作。
Buffer: 使沙盒内的代码能够处理二进制数据。
process: 提供关于当前 Node.js 进程的信息,但通常会对其做一些限制,以防止恶意代码影响主进程。
用户自定义变量和方法主要包括:
测试框架相关的函数:例如 test 和 expect,用于定义和执行测试。
自定义的 require 函数:用于加载模块,确保沙盒内的代码只能访问允许的模块。

如何从0构建一款类jest的工具

初始化项目

mkdir custom-jest-runtime
cd custom-jest-runtime
npm init -y
npm install @babel/core @babel/preset-env
npm install expect

实现文件发现和收集逻辑

下面的代码中遍历目录中所有以.test.js结尾的文件,并将所有文件path进行收集存储。

import { readdirSync, statSync } from 'fs';
import { join } from 'path';export const findTestFiles = (dir, testFiles = []) => {const files = readdirSync(dir);files.forEach((file) => {const filePath = join(dir, file);if (statSync(filePath).isDirectory()) {findTestFiles(filePath, testFiles);} else if (file.endsWith('.test.js')) {testFiles.push(filePath);}});return testFiles;
};

创建沙盒运行环境

这里使用node.js中的vm模块实现在沙盒环境中完成测试的编译的执行

import { Script, createContext } from 'vm';
import assert from 'assert';
export const runInSandbox = (code, context = {}) => {const sandbox = createContext({...context,assert,console,setTimeout,setInterval,clearTimeout,clearInterval,Buffer,process,});const script = new Script(code);script.runInContext(sandbox);
};

模块解析和加载

使用 Babel 转换现代 JavaScript 代码,保证不同版本Js代码兼容性。

import * as babel from '@babel/core';
import { readFileSync } from 'fs';export const loadModule = (filePath) => {const code = readFileSync(filePath, 'utf8');const { code: transformedCode } = babel.transformSync(code, {presets: ['@babel/preset-env'],});return transformedCode;
};

执行测试文件逻辑

在runTestFile中,首先调用前面封装的loadModule来对测试文件内容进行转换,以兼容不同版本的js代码,接着在context上下文中自定义了test对象,这样当code中包含test的时候,就能进行识别。最后调用runInSandbox执行。

import { runInSandbox } from './sandbox.js';
import { loadModule } from './moduleLoader.js';export const runTestFile = (testFile) => {const code = loadModule(testFile);const context = {test: (name, fn) => {try {fn();console.log(`Test passed: ${name}`);} catch (error) {console.log(`Test failed: ${name}`);console.error(error);}},};runInSandbox(code, context);
};
//index.js入口文件
import { findTestFiles } from './fileFinder.js';
import { runTestFile } from './testRuntime.js';const testFiles = findTestFiles(new URL('./tests', import.meta.url).pathname);
testFiles.forEach(runTestFile);

在tests目录下编写一个测试脚本,如下所示:

const sum = (a, b) => a + b;test('adds 1 + 2 to equal 3', () => {assert.strictEqual(sum(1, 2), 3);
});

运行index.js脚本(node index.js),得到如下结果,说明执行成功。

  以上就是实现一款类似jest框架的过程。jest框架本身会比这个复杂很大,它自身又集成了其他一些工具,例如expect包等。

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

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

相关文章

NetSuite Account Merge 科目合并功能分析

最近项目中,客户有提到过能否将不用的Account与新建的Account进行合并,即我们所说的Merge功能~可以,但是该功能有使用的限制,比如最直接的一点需要注意,不同类型的Account是不可以使用Merge功能的&#xff…

汽车软件开发者的必修课:ASPICE 4.0主要特点、优势及与之前版本的变化之处

ASPICE(汽车SPICE)4.0是专为汽车行业量身定制的过程评估模型,旨在确保软件和系统开发过程的质量和可靠性。它是更广泛的 ISO/IEC 330xx 系列标准的一部分,源自通用 SPICE(软件流程改进和能力确定)框架。 AS…

批归一化(Batch Normalization)和层归一化(Layer Normalization)的作用

在深度学习领域,归一化技术被广泛用于加速神经网络的训练速度并提高其稳定性。本文将介绍两种常见的归一化方法:批归一化(Batch Normalization, BN)和层归一化(Layer Normalization, LN),并通过…

ATA-7025高压放大器的优势如何

高压放大器是一类在电子领域中具有重要作用的设备,其主要功能是将输入信号的电压放大到更高的水平。在许多应用中,高压放大器展现出独特的优势,下面将介绍高压放大器的优势以及它们在不同领域的应用。 高压放大器的优势 1.信号驱动能力强 高压…

ATA-3040C功率放大器的基本要求包括什么

功率放大器是电子设备中常用的一个组件,用于将输入信号增强到足够大的电平,以驱动负载而不失真。要设计一个高效和性能优越的功率放大器,需要考虑多个基本要求和设计考虑因素。下面安泰电子将介绍功率放大器的基本要求,以及如何满…

中兴光猫破解telnet配置命令汇总

中兴光猫telnet配置命令汇总 | LogDicthttps://www.logdict.com/archives/zhong-xing-guang-mao-telnetpei-zhi-ming-ling-hui-zong

【王道数据结构笔记】单链表的基本操作之指定结点的后插操作(代码分析)

🎈个人主页:豌豆射手^ 🎉欢迎 👍点赞✍评论⭐收藏 🤗收录专栏:数据结构 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步! 【王道数据结构笔记】单链表的基本操作之指定结点的后插操作(代码分析) 引言一 代码二 分析总结…

【LeetCode:2741. 特别的排列 + 递归 + 记忆化搜索 + 动态规划】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

解决GPU 显存未能完全释放

一、 现象 算法同学反馈显存未能完全释放。 二、解决方法 一条命令搞定 注意:执行时注意不要误杀其他的python进程,需要确认好。 我的这条命令是将所有python进程都杀死了 ps -elf | grep python | awk {print $4} | xargs kill -s 9

使用AI技术实现语言练习

使用人工智能技术实现语言场景练习,可以有效地提高学习者的语言能力,包括口语、听力、阅读和写作。以下是一些常见的应用场景。北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交流合作。 1. 口语练习 虚拟对话伙伴: 利用…

FullCalendar日历组件集成实战(16)

背景 有一些应用系统或应用功能,如日程管理、任务管理需要使用到日历组件。虽然Element Plus也提供了日历组件,但功能比较简单,用来做数据展现勉强可用。但如果需要进行复杂的数据展示,以及互动操作如通过点击添加事件&#xff0…

Linux上使用 git 命令行

在 Github或者 gitee 注册账号 这个比较简单 , 参考着官网提示即可 . 需要进行邮箱校验.以下以创建Github为例。 创建项目 1. 登陆成功后 , 进入个人主页 , 点击下方的 create a new repository 按钮新建项目 2. 在创建好的项目页面中复制项目的链接 , 以备接下来进行下…

10分钟掌握Python缓存,效率提升1000%

全文速览 python的不同缓存组件的使用场景和使用样例cachetools的使用 项目背景 代码检查项目,需要存储每一步检查的中间结果,最终把结果汇总并写入文件中 在中间结果的存储中 可以使用context进行上下文的传递,但是整体对代码改动比较大…

Linux系统中文件权限详解

一、Linux文件权限设计 Linux系统中任何内容都可以用文件表示,其对文件设计了一套权限进行管理;文件权限共有11个字符,从左向右共分为5段(每段的具体说明如下表Linux权限设计说明所示): Linux权限设计说明 …

揭秘:企业如何防盗版软件

在当前的数字化时代,软件盗版问题一直困扰着软件开发者和企业。为了维护软件的合法权益,保护创新成果,许多企业采取了各种技术手段来防止软件被非法复制和分发。其中,白名单机制作为一种有效的防盗版软件手段,被广泛采…

书酒共舞:品味文字之韵,沉醉酒香之境

在喧嚣的都市中,我们常常渴望找到一片宁静的角落,让心灵得到片刻的休憩。此刻,一杯雷盛红酒与一本心仪的书籍,便成了很好的伴侣,它们相互映衬,共同编织出一幅优雅的画卷,让我们在品味中感受文字…

数据处理神器Elasticsearch_Pipeline:原理、配置与实战指南

文章目录 📑引言一、Elasticsearch Pipeline的原理二、Elasticsearch Pipeline的使用2.1 创建 Pipeline2.2 使用 Pipeline 进行索引2.3 常用的 Processor 三、实际应用场景3.1 日志数据处理3.2 数据清洗和标准化3.3 数据增强 四、最佳实践4.1 性能优化4.2 错误处理4…

Fusion WAN:企业出海与全球组网的数字网络底座

众多中国企业与品牌正将目光投向海外市场,积极寻求发展新机遇,并且在这一过程中取得了显著的成果。"出海"战略已经成为一些企业转型升级的关键选择。 随着国内市场的竞争日益激烈,越来越多的企业开始寻求海外市场的拓展&#xff0c…

11年之约 聚焦上海 | 亚信科技邀您相约2024 MWC上海展

关于亚信安慧AntDB数据库 AntDB数据库始于2008年,在运营商的核心系统上,服务国内24个省市自治区的数亿用户,具备高性能、弹性扩展、高可靠等产品特性,峰值每秒可处理百万笔通信核心交易,保障系统持续稳定运行超十年&a…

深入理解PHP命名空间

在PHP项目中,命名空间(namespace)是一个非常重要的特性。它不仅帮助开发者组织代码,还能避免类、函数、常量等命名冲突问题。本文将详细介绍PHP命名空间的概念、使用方法和最佳实践。 一、什么是命名空间? 命名空间…