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

news2024/11/17 2:54:41

 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.coloradmin.cn/o/1864545.html

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

相关文章

Jackson和fastjson解决序列化时字段属性大小写改变的问题

在部分特殊场景下,我们可能会把实体的字段属性改成全部大写,但是在返回前端时,字段会被序列化成小写。 比如我们有一个这个类属性都是大写: 后端接口是这样的 然后我们请求后,会发现我们的字段被变成全部小写的。 …

Baidu Comate最详细教程(含license码)!扫二维码即可得文心4.0 VIP90天免费使用

Baidu Comate最详细教程(含license码)!扫二维码即可得文心4.0 VIP90天免费使用 安装 方法一:点击链接 comate安装链接 方法二:扫描图片二维码 支持所有常见IDE,包括VSCode、JetBrains系列(包含…

【课程作业】嵌入式系统与设计上机作业(作业九十)

个人名片: 🎓作者简介:嵌入式领域优质创作者🌐个人主页:妄北y 📞个人QQ:2061314755 💌个人邮箱:[mailto:2061314755qq.com] 📱个人微信:Vir2025WB…

Qt实战项目——贪吃蛇

一、项目介绍 本项目是一个使用Qt框架开发的经典贪吃蛇游戏,旨在通过简单易懂的游戏机制和精美的用户界面,为玩家提供娱乐和编程学习的机会。 二、主要功能 2.1 游戏界面 游戏主要是由三个界面构成,分别是游戏大厅、难度选择和游戏内界面&a…

three.js 第六节 - 纹理以及贴图【.hdr文件(hdr贴图)】- 色彩空间

素材 这是素材 更多素材、案例、项目 好几个G一共,加我q178373168,60大洋拿走 源码 源码 // ts-nocheck // 引入three.js import * as THREE from three // 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls…

渗透测试工程师常见面试33题

问1:关于sql注入,都分为那些? 答1:主要分为两个大类,有回显和无回显。其中无回显的称为盲注,包括时间盲注、DNSlog注入也算一种,布尔盲注;有回显的包括联合注入、报错注入、宽字节注…

存储故障导致的Oracle 19c 数据库集群无法启动案例分析

背景 在某周末11点多,突然收到批量数据库宕机告警信息,同时收到多个微信群数据库无法连接、访问报错的反馈,顿时感觉这是出大事了。 故障分析 登录环境后,查看数据库alert日志,日志中出现数据库不可用信息&#xff…

python-登录界面-demo

文章目录 前言python-登录界面-demo 前言 如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差,实在白嫖的话,那欢迎常来啊!!! python-…

免费分享:中国1:250万地质图(附下载方法)

中国1:250万地质图反映了中国区域地质构造面貌和基本特征,表示了中国区域地质的特色,地质编图的标准化程度较高,地质图的编制过程中充分采用了信息技术,反映了地质调查与科研的若干新进展与认识。 数据简介 本数据为空…

Firefox 编译指南2024 Windows10篇- 源码获取(二)

1. 引言 在成功准备了编译环境之后,下一步就是获取Firefox的源码。源码是编译任何软件的基础,对于开源项目如Firefox尤其重要。通过获取并理解源码,开发者不仅能够编译出自定义版本的Firefox,还能对其进行修改和优化,…

坚持100天学习打卡Day1

1.大小端 2.引用的本质 及 深拷贝与浅拷贝 3.初始化列表方式 4.类对象作为类成员 5.静态成员 static

中国杀出全球首个烹饪大模型

什么?烹饪也有大模型?! 没有听错,这就是国产厨电龙头老板电器最新发布——“食神”大模型。 数十亿级行业数据,数千万级知识图谱加持,据称还是全球首个。 它能为每个人提供个性化量身定制的解决方案&…

Pixel手机中文网-全球最大的华人Pixel手机论坛

Pixel手机中文网,使用Pixel手机华人的聚集地,快来加入这个大家庭分享和创作吧 😃 googlepixel.cn

信息系统安全风险评估,让风险看得见!

信息安全风险管理依据等级保护的思想和适度安全的原则,平衡成本与效益,合理部署和利用信息安全的信任体系、监控体系和应急处理等重要的基础设施,确定合适的安全措施,从而确保机构具有完成其使命的信息安全保障能力。亿林依据《国…

武汉星起航:国家政策助力亚马逊电商平台蓬勃发展

在全球化浪潮和数字经济快速发展的双重推动下,跨境电商已成为推动国际贸易增长的新引擎。武汉星起航持续关注到,亚马逊电商平台作为其中的佼佼者,在享受国家政策红利的同时,也展现出了强大的发展活力和市场潜力。 近年来&#xf…

JavaScript的学习之文档的加载

目录 一、onload的运用 浏览器在加载一个页面时,是按照自上而下的顺序加载的,读取到一行就执行一行, 如果script标签写到页面的上方,在代码执行时,页面还没有加载,所以要将JS代码写道页面下面 一、onload的…

代码随想录第35天|动态规划

理论基础 动态规划是由前一个状态推导出来的, 而贪心是局部直接选取最优 五部曲: 确定dp数组(dp table)以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 debug过程 : dp数组打印查看 509. 斐波那契数 参考 //动态规划的方法 …

科普:什么是 BC-404 ?全方位解读最新通缩型 NFT 标准

区块链技术飞速发展的今天,创新从未停歇。继 ERC-404 标准问世后,一个名为 BC-404 的新标准应运而生,为 NFT 市场带来了全新的可能性。BC-404(Bonding Curve 404)—基于对 ERC-404 的改进,加密货币中第一个…

【大模型】大模型微调方法总结(二)

1.Adapter Tuning 1.背景 2019年谷歌的研究人员首次在论文《Parameter-Efficient Transfer Learning for NLP》提出针对 BERT 的 PEFT微调方式,拉开了 PEFT 研究的序幕。他们指出,在面对特定的下游任务时,如果进行 Full-Fintuning&#xff0…

基于Simulink的行波故障测距

基于MATLAB/SIMULINK的输电线路故障行波仿真方法 为了更深入地学习和研究输电线路故障行波,通过matlab将复杂的电力系统简化为一个等效电路模型,使得故障的仿真和行波的提取更加直观和方便。首先,我们根据电力系统的实际情况,建立…