使用node内置test runner,和 Jest say 拜拜
参考 https://nodejs.org/dist/latest-v20.x/docs/api/test.html#test-runner
在之前,我们写单元测试,必须安装第三方依赖包,而从node 20.0.0
版本之后,可以告别繁琐的第三方依赖包啦,可直接使用node的内置test runner。相关功能在20.0.0
版本后才稳定,要使用test runner
请安装node版本>=20.0.0
。
使用:
import test from 'node:test';
注意必须使用node:test
,下面的代码将不会生效:
import test from 'test';
通过test
模块创建的测试由单个函数组成,该函数以以下三种方式之一进行处理:
- 同步函数,如果抛出异常则认为失败,否则认为通过。
- 返回
Promise
的函数,如果Promise rejects,则认为失败,如果resolves则认为通过。 - 接收回调函数的函数,如果回调函数的第一个参数是
true
,则认为失败,如果是false
,则认为通过。如果测试函数接收的回调函数也返回Promise
,则测试将失败。
示例:
- 同步函数,未抛出异常,测试通过
test('synchronous passing test', (t) => {assert.strictEqual(1, 1);
});
- 同步函数,抛出异常,测试失败
test('synchronous failing test', (t) => {assert.strictEqual(1, 2);
});
- 返回Promise的函数,Promise resolves,测试通过
test('asynchronous passing test', async (t) => {assert.strictEqual(1, 1);
});
- 返回Promise的函数,Promise rejected,测试失败
test('asynchronous failing test', async (t) => {assert.strictEqual(1, 2);
});
- 直接使用Promise rejected,测试失败
test('failing test using Promises', (t) => {return new Promise((resolve, reject) => {setImmediate(() => {reject(new Error('this will cause the test to fail'));});});
});
- 使用
setImmediate()
调用done()
,不传参
test('callback passing test', (t, done) => {setImmediate(done);
});
- 使用
setImmediate()
调用done()
,传参Error
对象,测试失败。
test('callback failing test', (t, done) => {setImmediate(() => {done(new Error('callback failure'));});
});
如果任何测试失败,进程退出码会被设置为1。
子测试
test()
方法允许创建子测试。此方法的行为与顶层test()
函数相同。下面的示例演示了如何创建包含两个子测试的顶级测试。
test('top level test', async (t) => {await t.test('subtest 1', (t) => {assert.strictEqual(1, 1);});await t.test('subtest 2', (t) => {assert.strictEqual(2, 2);});
});
在本例中,await
用于确保两个子测试都已完成。这是必要的,因为父测试不等待其子测试完成。当父测试完成时仍然未完成的任何子测试将被取消并视为失败。任何子测试失败都会导致父测试失败。
跳过测试
可以通过将skip
属性传递给测试,或通过调用测试上下文的skip()
方法跳过单个测试,如下面的示例所示。
- 跳过该测试选项,但不提供任何提示。
test('skip option', { skip: true }, (t) => {// 这里的代码不会执行
});
- 跳过该测试选项,提供提示。
test('skip option with message', { skip: 'this is skipped' }, (t) => {// 这里的代码不会执行
});
- 如果测试包含额外的逻辑,请确保返回到正确的位置。
test('skip() method', (t) => {// 返回额外的逻辑t.skip();
});test('skip() method with message', (t) => {// 返回额外的逻辑t.skip('this is skipped');
});
describe
/it
语法
运行测试也可以使用describe
来声明一个套件,使用it
来声明一个测试。套件用于组织和分组相关的测试。it
是test()
的简写。
describe('A thing', () => {it('should work', () => {assert.strictEqual(1, 1);});it('should be ok', () => {assert.strictEqual(2, 2);});describe('a nested thing', () => {it('should work', () => {assert.strictEqual(3, 3);});});
});
describe
和it
是从node:test
模块导入的。
import { describe, it } from 'node:test';
only
测试
如果Node.js以--test-only
命令行启动,则可以通过将only
属性传递给应该运行的测试来跳过除选定子集之外的所有顶级测试。当运行具有only
属性集的测试时,也会运行所有子测试。测试上下文的runOnly()
方法可用于在子测试级别实现相同的行为。
// 假设 Node.js 使用 --test-only 命令行运行
// 设置了“only”属性,此测试会运行
test('this test is run', { only: true }, async (t) => {// 在此测试中,默认运行所有子测试await t.test('running subtest');// 可以使用“only”属性更新测试上下文,只运行子测试中有only属性的测试t.runOnly(true);await t.test('this subtest is now skipped');await t.test('this subtest is run', { only: true });// 切换上下文回执行所有子测试t.runOnly(false);await t.test('this subtest is now run');// 明确不运行以下测试await t.test('skipped subtest 3', { only: false });await t.test('skipped subtest 4', { skip: true });
});
// 未设置“only”选项,此测试会被跳过
test('this test is not run', () => {// This code is not run.throw new Error('fail');
});
按名称筛选测试
--test-name-pattern
命令行选项可用于仅运行名称与提供的模式匹配的测试。测试名字模式被解释为JavaScript正则表达式。可以多次指定--test-name-pattern
选项,以便嵌套测试。对于执行的每个测试,也会运行任何相应的测试钩子,例如beforeEach()
。
给定以下测试文件,使用--test-name-pattern="test[1-3]"
选项启动Node.js将导致测试运行器执行test 1
、test 2
和test 3
。如果test 1
不匹配测试名称,那么它的子测试就算名称匹配也不会执行。同一组测试也可以通过多次传递--test-name-pattern
来执行(例如--test-name-pattern="test 1"
,--test-name-pattern="test 2"
,等等)。
test('test 1', async (t) => {await t.test('test 2');await t.test('test 3');
});test('Test 4', async (t) => {await t.test('Test 5');await t.test('test 6');
});
测试名模式也可以使用正则表达式字面量指定。这允许使用正则表达式标志。在前面的例子中,用--test-name-pattern="/test [4-5]/i"
启动Node.js会匹配Test 4
和Test 5
,因为这个模式是不区分大小写的。
测试名称模式不会更改测试运行器执行的文件集。
无关的异步活动
一旦测试函数执行完成,就会在保持测试顺序的同时尽可能快地报告结果。然而,测试函数有可能生成比测试本身更长久的异步活动。测试运行器处理这种类型的活动,但是不会为了适应它而延迟测试结果的报告。
在下面的示例中,一个测试完成时,两个setImmediate()
操作仍然未执行。第一个setImmediate()
尝试创建一个新的子测试。因为父测试已经完成并输出其结果,所以新的子测试立即被标记为失败,并稍后报告给<TestsStream>
。
第二个setImmediate()
创建一个uncaughtException
事件。来自已完成测试的uncaughtException
和unhandledRejection
事件被test
模块标记为失败,并由<TestsStream>
在顶层作为诊断警告报告。
test('a test that creates asynchronous activity', (t) => {setImmediate(() => {t.test('subtest that is created too late', (t) => {throw new Error('error1');});});setImmediate(() => {throw new Error('error2');});// 测试在此行之后结束
});
从命令行运行测试
Node.js测试运行器可以通过传递--test
标志从命令行调用:
node --test
默认情况下,Node.js将递归地在当前目录中搜索匹配特定命名约定的JavaScript源文件。匹配的文件作为测试文件执行。有关预期的测试文件命名约定和行为的更多信息,可以在测试运行器执行模型部分中找到。
或者,可以将一个或多个路径作为Node.js命令的最后一个参数,如下所示。
node --test test1.js test2.mjs custom_test_dir/
在本例中,测试运行器将执行文件test1.js
和test2.mjs
。测试运行器还将递归地在custom_test_dir/
目录中搜索要执行的测试文件。
测试运行器执行模型
当搜索要执行的测试文件时,测试运行程序的行为如下:
-
执行用户显式提供的任何文件。
-
如果用户没有显式指定任何路径,则按照以下步骤中指定的方式递归地搜索当前工作目录中的文件。
-
node_modules
目录将被跳过,除非用户显式指定。 -
如果遇到一个名为
test
的目录,测试运行器将递归地搜索所有.js
、.cjs
和.mjs
文件。所有这些文件都被视为测试文件,并且不需要匹配下面详细介绍的特定命名约定。这是为了适应将所有测试用例放在一个test
目录中的项目。 -
在所有其他目录中,
.js
,.cjs
和.mjs
文件匹配以下模式被视为测试文件:^test$
-文件名为字符串'test'
的文件。示例:test.js
,test.cjs
,test.mjs
。^test-.+
-文件名以字符串'test-'
开头,后跟一个或多个字符的文件。示例:test-example.js
、test-another-example.mjs
。+[.-_]test$
-文件名以.test
、-test
或_test
结尾,前面有一个或多个字符的文件。示例:example.test.js
、example-test.cjs
,example_test.mjs
。- Node.js理解的其他文件类型,如
.node
和.json
,不会被测试运行器自动执行,但如果在命令行上显式提供,则会得到支持。
每个匹配的测试文件在单独的子进程中执行。如果子进程结束时退出代码为0,则认为测试通过。否则,测试被认为是失败的。测试文件必须由node .js执行,但不需要在内部使用node:test
模块。
node:test
模块支持在测试期间通过顶级mock
对象进行模拟。下面的示例创建一个监视函数,该函数将两个数字相加,然后使用spy来断言函数是否按预期调用。
import assert from 'node:assert';
import { mock, test } from 'node:test';test('spies on a function', () => {const sum = mock.fn((a, b) => {return a + b;});assert.strictEqual(sum.mock.calls.length, 0);assert.strictEqual(sum(3, 4), 7);assert.strictEqual(sum.mock.calls.length, 1);const call = sum.mock.calls[0];assert.deepStrictEqual(call.arguments, [3, 4]);assert.strictEqual(call.result, 7);assert.strictEqual(call.error, undefined);// 重置全局跟踪的mockmock.reset();
});
同样的模拟功能也暴露在每个测试的TestContext对象上。下面的示例使用TestContext
上公开的API在对象方法上创建一个spy。通过测试上下文进行模拟的好处是,一旦测试结束,测试运行器将自动恢复所有模拟的功能。
test('spies on an object method', (t) => {const number = {value: 5,add(a) {return this.value + a;},};t.mock.method(number, 'add');assert.strictEqual(number.add.mock.calls.length, 0);assert.strictEqual(number.add(3), 8);assert.strictEqual(number.add.mock.calls.length, 1);const call = number.add.mock.calls[0];assert.deepStrictEqual(call.arguments, [3]);assert.strictEqual(call.result, 8);assert.strictEqual(call.target, undefined);assert.strictEqual(call.this, number);
});
Timers
模拟计时器是软件测试中常用的一种技术,用于模拟和控制计时器的行为,例如setInterval
和setTimeout
,而无需实际等待指定的时间间隔。
有关方法和特性的完整列表,请参考MockTimers类。
这允许开发人员为依赖时间的功能编写更可靠和可预测的测试。
下面的例子展示了如何模拟setTimeout
。使用.enable(['setTimeout']);
它将模拟node:timers
和node:timers/promises
模块中的setTimeout
函数,以及node .js全局上下文中的setTimeout函数。
注意: 此API目前不支持import {setTimeout} from 'node:timers'
等解构函数。
import assert from 'node:assert';
import { mock, test } from 'node:test';test('mocks setTimeout to be executed synchronously without having to actually wait for it', () => {const fn = mock.fn();// 选择要模拟的内容mock.timers.enable(['setTimeout']);setTimeout(fn, 9999);assert.strictEqual(fn.mock.callCount(), 0);// 推进时间mock.timers.tick(9999);assert.strictEqual(fn.mock.callCount(), 1);// 重置全局跟踪的mockmock.timers.reset();// 如果调用reset mock instance,它也会重置计时器实例mock.reset();
});
同样的模拟功能也暴露在每个测试的TestContext对象的mock属性中。通过测试上下文进行模拟的好处是,一旦测试结束,测试运行器将自动恢复所有模拟计时器功能。
import assert from 'node:assert';
import { test } from 'node:test';test('mocks setTimeout to be executed synchronously without having to actually wait for it', (context) => {const fn = context.mock.fn();// 选择要模拟的内容context.mock.timers.enable(['setTimeout']);setTimeout(fn, 9999);assert.strictEqual(fn.mock.callCount(), 0);// 推进时间context.mock.timers.tick(9999);assert.strictEqual(fn.mock.callCount(), 1);
});
测试reporter
node:test
模块支持传递--test-reporter
标志,让测试运行器使用特定的reporter。
支持以下内置reporter:
tap
:tap
reporter 以TAP格式输出测试结果。spec
:spec
reporter 以人类可读的格式输出测试结果。dot
:dot
reporter 以紧凑的格式输出测试结果,其中通过的测试用.
表示,失败的测试用X
表示。
当sdout
是TTY时,默认情况下使用spec
reporter。否则,默认使用tap
reporter。
这些 reporter 的确切输出可能会在Node.js的不同版本之间发生变化,不应该以编程方式依赖于它们。如果需要对测试运行器的输出进行编程访问,请使用发出的事件。
reporter 可通过node:test/reports
模块获得:
import { tap, spec, dot } from 'node:test/reporters';
自定义reporter
--test-reporter
可以用来指定自定义 reporter 的路径。自定义 reporter 是一个导出stream.compose接受的值的模块。reporter 应该转换由发出的事件。
使用<stream.Transform>的自定义 reporter 的示例:
import { Transform } from 'node:stream';const customReporter = new Transform({writableObjectMode: true,transform(event, encoding, callback) {switch (event.type) {case 'test:start':callback(null, `test ${event.data.name} started`);break;case 'test:pass':callback(null, `test ${event.data.name} passed`);break;case 'test:fail':callback(null, `test ${event.data.name} failed`);break;case 'test:plan':callback(null, 'test plan');break;case 'test:diagnostic':callback(null, event.data.message);break;case 'test:coverage': {const { totalLineCount } = event.data.summary.totals;callback(null, `total line count: ${totalLineCount}\n`);break;}}},
});export default customReporter;
提供给--test-reporter
的值应该是一个字符串,类似于JavaScript代码中import()
中使用的字符串,或者是提供给--import
的值。
多种reporter
可以多次指定--test-reporter
标志,以多种格式报告测试结果。在这种情况下,需要使用--test-reporter-destination
为每个报告程序指定一个目标。目标可以是stdout
、stderr
或文件路径。reporter和目标按照指定的顺序配对。
在下面的例子中,spec
reporter 将输出到 stdout
, dot
reporter 将输出到file.txt
:
node --test-reporter=spec --test-reporter=dot --test-reporter-destination=stdout --test-reporter-destination=file.txt
当指定了单个reporter时,除非显式地提供了目标,否则将默认为stdout
。
相关文章:

使用node内置test runner,和 Jest say 拜拜
参考 https://nodejs.org/dist/latest-v20.x/docs/api/test.html#test-runner 在之前,我们写单元测试,必须安装第三方依赖包,而从node 20.0.0 版本之后,可以告别繁琐的第三方依赖包啦,可直接使用node的内置test runner…...

《面试1v1》Kafka的架构设计是什么样子
🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结…...

比较常见CPU的区别:Intel、ARM、AMD
一、开发公司不同 1、Intel:是英特尔公司开发的中央处理器,有移动、台式、服务器三个系列。 2、ARM:是英国Acorn有限公司设计的低功耗成本的第一款RISC微处理器。 3、AMD:由AMD公司生产的处理器。 二、技术不同 1、Intel&…...

CAN转EtherNet/IP网关can协议是什么意思
你是否曾经遇到过不同的总线协议难以互相通信的问题?远创智控的YC-EIP-CAN网关为你解决了这个烦恼! 远创智控YC-EIP-CAN通讯网关是一款自主研发的设备,它能够将各种CAN总线和ETHERNET/IP网络连接起来,解决不同总线协议之间的通信…...

java可变字符序列:StringBuffer、StringBuilder
文章目录 StringBuffer与StringBuilder的理解StringBuilder、StringBuffer的API StringBuffer与StringBuilder的理解 因为String对象是不可变对象,虽然可以共享常量对象,但是对于频繁字符串的修改和拼接操作,效率极低,空间消耗也…...

Mac/win开发快捷键、vs插件、库源码、开发中的专业名词
目录 触控板手势(2/3指) 鼠标右键 快捷键 鼠标选择后shift⬅️→改变选择 mac command⬅️:删除←边的全部内容 commadtab显示下栏 commandshiftz向后撤回 commandc/v复制粘贴 command ⬅️→回到行首/末 commandshift3/4截图 飞…...

linux 系统编程
C标准函数与系统函数的区别 什么是系统调用 由操作系统实现并提供给外部应用程序的编程接口。(Application Programming Interface,API)。是应用程序同系统之间数据交互的桥梁。 一个helloworld如何打印到屏幕。 每一个FILE文件流(标准C库函数ÿ…...

Python策略模式介绍、使用方法
一、Python策略模式介绍 Python策略模式(Strategy Pattern)是一种软件设计模式,用于通过将算法封装为独立的对象,而使得它们可以在运行时动态地相互替换。该模式使得算法的变化独立于使用它们的客户端,从而达到代码的…...

城市气象数据可视化:洞察气候变化,构建智慧城市
随着城市化进程的加速,城市气象数据的采集和分析变得越来越重要。气象数据不仅影响着人们的生活和出行,还与城市的发展和规划息息相关。在数字化时代,如何将城市中各个气象数据进行可视化,让复杂的数据变得简单易懂,成…...

Rust-IO
use std::io::Write; fn main() {/*std::io::stdin() 返回标准输入流stdin的句柄。read_line() stdin的句柄的一个方法,从标准输入流中读取一行数据返回一个Result枚举。会自动删除行尾的换行符\n。unwrap() 是一个帮助的方法,简化恢复错误的处理。返回R…...

cp -r 源目录 目标目录
在Linux中,要复制目录可以使用cp命令。cp命令用于复制文件和目录。要复制整个目录及其内容,可以使用 -r 或 --recursive 参数来递归地复制目录。以下是示例命令:bash cp -r 源目录 目标目录其中: 源目录是要复制的目录的路径。目…...

redis之Bitmap
位图数据结构其实并不是一个全新的玩意,我们可以简单的认为就是个数组,只是里面的内容只能为0或1而已(二进制位数组)。 GETBIT用于返回位数组在偏移量上的二进制位的值。值得我们注意的是,GETBIT的时间复杂度是O(1)。 GETBIT命令的执行过程如…...

建设数据中台到底有啥用?
最近专注在数据和人工智能领域,从数据仓库、商业智能、主数据管理到大数据平台的建设,经过很多项目的沉淀和总结,最后我和团队一起总结了精益数据创新的体系。一直战斗在企业信息化一线。 企业为什么要建设数据中台,数据中台对于…...

[运维|系统] Centos设置本地编码
以下是在CentOS上更改系统编码的一般步骤: 使用locale命令查看当前的系统编码: locale如果需要更改系统编码,可以使用类似下面的命令来生成相应的locale设置(以UTF-8为例): sudo localedef -i en_US -f …...

深入探索Python中的os.listdir函数
深入探索Python中的os.listdir函数 1. 引言 在Python中,文件和目录操作是常见的任务之一。而os.listdir()函数是Python中用于获取指定目录下所有文件和子目录的函数之一。本篇博客将深入探索os.listdir()函数的用法和注意事项。 2. os模块简介 Python的os模块是…...

ROS1ROS2之CmakeList.txt和package.xml用法详解
前言:目前还在学习ROS无人机框架中,,, 更多更新文章详见我的个人博客主页【前往】 文章目录 1. CMakeLists.txt与package.xml的作用2. 生成CMakeLists.txt2.1 ROS12.2 ROS2 3. CMakeLists.txt编写3.1 ROS13.2 ROS2 4. package.xml…...

C#设计模式之---适配器模式
适配器模式(Adapter Pattern) 适配器模式(Adapter Pattern)也称包装样式或者包装(wrapper)。将一个类的接口转接成用户所期待的。适配器模式是一种结构型模式,一个适配使得因接口不兼容而不能在一起工作的类工作在一起…...

串口设备驱动
文章目录 一、串口简介二、Linux下串口驱动框架uart_driver 结构体uart_port 的添加与移除三、Linux下串口驱动工作流程四、Linux下串口应用开发终端工作模式多线程例程一、串口简介 串口全称叫做串行接口,通常也叫做 COM 接口,串行接口指的是数据一个一个的顺序传输,通信线…...

Nginx实现反向代理和负载均衡
Nginx安装 本文章主要介绍下,如何使用Nginx来实现反向代理和负载均衡,Nginx安装和基础知识,可参考我的这篇文章 Nginx安装。 Nginx实现反向代理 实现反向代理需要准备两台Nginx服务器。一台Nginx服务器A,ip为 192.168.206.140&…...

小米手机MIUI优化的影响
1. 小/红米手机的MIUI优化选项 2. MIUI优化选项的影响 2.1 MIUI优化会影响应用信息展示 MIUI优化选项会影响到应用信息的内容展示,具体如下图所示: 如果我们需要在应用信息里展示自启动入口,那我们就需要开启MIUI优化。 2.2 MIUI优化会影…...

【图论】kruskal算法
一.介绍 Kruskal(克鲁斯卡尔)算法是一种用于解决最小生成树问题的贪心算法。最小生成树是指在一个连通无向图中,选择一棵包含所有顶点且边权重之和最小的树。 下面是Kruskal算法的基本步骤: 将图中的所有边按照权重从小到大进行…...

Django框架:使用channels实现websocket,配置和项目实际使用
一、基本配置 依赖包: Django3.2 django-cors-headers3.5.0 redis4.6.0 #操作redis数据库的 channels3.0.0 #websocket channels-redis4.1.0 #通道层需要,依赖redis包项目目录结构: study_websocket --study_websocket --__init__.py --s…...

基于RK3588+FPGA+AI算法定制的智慧交通与智能安防解决方案
随着物联网、大数据、人工智能等技术的快速发展,边缘计算已成为当前信息技术领域的一个热门话题。在物联网领域,边缘计算被广泛应用于智慧交通、智能安防、工业等多个领域。因此,基于边缘计算技术的工业主板设计方案也受到越来越多人的关注。…...

AI面试官:LINQ和Lambda表达式(一)
AI面试官:LINQ和Lambda表达式(一) 当面试官面对C#中关于LINQ和Lambda表达式的面试题时,通常会涉及这两个主题的基本概念、用法、实际应用以及与其他相关技术的对比等。以下是一些可能的面试题目,附带简要解答和相关案…...

FPGA学习——FPGA利用状态机实现电子锁模拟
文章目录 一、本次实验简介二、源码及分析三、总结 一、本次实验简介 本次是实验是为了利用状态机模拟电子锁,相关要求如下: 顺序输入4位密码,密码为1234,用按键来键入密码用led灯指示键入第几位密码,(博…...

Bert经典变体学习
ALBert ALBERT就是为了解决模型参数量大以及训练时间过长的问题。ALBERT最小的参数只有十几M, 效果要比BERT低1-2个点,最大的xxlarge也就200多M。可以看到在模型参数量上减少的还是非常明显的,但是在速度上似乎没有那么明显。最大的问题就是这种方式其实…...

uniapp checkbox radio 样式修改
文章目录 通过查看代码,发现 before部分是设置样式的主要属性 我们要设置的话,就要设置checkbox::before的属性。 其中的content表示内容,比如内部的对勾 那么我们设置的时候,比如设置disabletrue的时候或者checkedtrue的时候&…...

电脑重启后VScode快捷方式失效,找不到Code.exe
问题描述 下班回家关了部分程序就直接关机了,回家后重启电脑发现vscode的快捷方式就失效了,提示Code.exe已被移动或删除。 解决方法 查看你的vscode安装目录,Microsoft VS Code目录下大概率会存在一个名为_的文件夹,然后会发现…...

C语言实现扫雷游戏
test.c源文件 - 扫雷游戏测试 game.h头文件 - 扫雷游戏函数的声明 game.c源文件 - 扫雷游戏函数的实现 1.布置雷 -- 存放雷的雷盘 9*9 数组设计成11*11 上下左右方各多一行,保证周围8的范围 雷 - 1 不是雷 - 0 2.排查雷 主题测试源文件代码 &…...

蓝图节点编辑器
打印字符串 第02章 蓝图结构 03 -注释和重新路由_哔哩哔哩_bilibili 第02章 蓝图结构 04 - 变量_哔哩哔哩_bilibili 第03章 蓝图简易门 01 - 箱子碰撞_哔哩哔哩_bilibili 第03章 蓝图简易门 02 - 静态Mesh和箭头_哔哩哔哩_bilibili 第03章 蓝图简易门 03 - 设置相对旋转节点_哔…...