使用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模块创建的测试由单个函数组成,该函数以以下三种方式之一进行处理:

  1. 同步函数,如果抛出异常则认为失败,否则认为通过。
  2. 返回Promise的函数,如果Promise rejects,则认为失败,如果resolves则认为通过。
  3. 接收回调函数的函数,如果回调函数的第一个参数是true,则认为失败,如果是false,则认为通过。如果测试函数接收的回调函数也返回Promise,则测试将失败。

示例:

  1. 同步函数,未抛出异常,测试通过
test('synchronous passing test', (t) => {assert.strictEqual(1, 1);
});
  1. 同步函数,抛出异常,测试失败
test('synchronous failing test', (t) => {assert.strictEqual(1, 2);
});
  1. 返回Promise的函数,Promise resolves,测试通过
test('asynchronous passing test', async (t) => {assert.strictEqual(1, 1);
});
  1. 返回Promise的函数,Promise rejected,测试失败
test('asynchronous failing test', async (t) => {assert.strictEqual(1, 2);
});
  1. 直接使用Promise rejected,测试失败
test('failing test using Promises', (t) => {return new Promise((resolve, reject) => {setImmediate(() => {reject(new Error('this will cause the test to fail'));});});
});
  1. 使用setImmediate()调用done(),不传参
test('callback passing test', (t, done) => {setImmediate(done);
});
  1. 使用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()方法跳过单个测试,如下面的示例所示。

  1. 跳过该测试选项,但不提供任何提示。
test('skip option', { skip: true }, (t) => {// 这里的代码不会执行
});
  1. 跳过该测试选项,提供提示。
test('skip option with message', { skip: 'this is skipped' }, (t) => {// 这里的代码不会执行
});
  1. 如果测试包含额外的逻辑,请确保返回到正确的位置。
test('skip() method', (t) => {// 返回额外的逻辑t.skip();
});test('skip() method with message', (t) => {// 返回额外的逻辑t.skip('this is skipped');
}); 

describe/it 语法

运行测试也可以使用describe来声明一个套件,使用it来声明一个测试。套件用于组织和分组相关的测试。ittest()的简写。

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);});});
});

describeit是从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 1test 2test 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 4Test 5,因为这个模式是不区分大小写的。

测试名称模式不会更改测试运行器执行的文件集。

无关的异步活动

一旦测试函数执行完成,就会在保持测试顺序的同时尽可能快地报告结果。然而,测试函数有可能生成比测试本身更长久的异步活动。测试运行器处理这种类型的活动,但是不会为了适应它而延迟测试结果的报告。

在下面的示例中,一个测试完成时,两个setImmediate()操作仍然未执行。第一个setImmediate()尝试创建一个新的子测试。因为父测试已经完成并输出其结果,所以新的子测试立即被标记为失败,并稍后报告给<TestsStream>

第二个setImmediate()创建一个uncaughtException事件。来自已完成测试的uncaughtExceptionunhandledRejection事件被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.jstest2.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.jstest-another-example.mjs
    • +[.-_]test$ -文件名以.test-test_test结尾,前面有一个或多个字符的文件。示例:example.test.jsexample-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

模拟计时器是软件测试中常用的一种技术,用于模拟和控制计时器的行为,例如setIntervalsetTimeout,而无需实际等待指定的时间间隔。

有关方法和特性的完整列表,请参考MockTimers类。

这允许开发人员为依赖时间的功能编写更可靠和可预测的测试。

下面的例子展示了如何模拟setTimeout。使用.enable(['setTimeout']);它将模拟node:timersnode: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为每个报告程序指定一个目标。目标可以是stdoutstderr或文件路径。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

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

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

相关文章

centos中修改防火墙端口开放配置

1、直接进入文件修改 vim /etc/sysconfig/iptables 2、添加需要开放的端口 &#xff08;1&#xff09;添加需要开放的单个端口 4001 -A INPUT -m state --state NEW -m tcp -p tcp --dport 4001 -j ACCEPT &#xff08;2&#xff09;添加需要开放的某个网段端口 4001:4020 …

需求管理中最易忽视的6大重点

需求管理是产品经理的重点工作&#xff0c;如果无法有效进行需求管理&#xff0c;往往会引起需求变更、项目延期以及成本增加等问题。那么如何对需求进行高效管理&#xff0c;我们在需求管理中&#xff0c;往往最容易忽视的重点都有哪些&#xff1f; 1、重视项目整体管理计划 首…

VMWare虚拟机常用操作命令

今日一语&#xff1a;做到所有的细节都不放过&#xff0c;则可以避免99%已知的风险&#xff0c;但大多数都因懒惰而甘愿承受风险&#xff0c;至此悔不当初 查看虚拟机在本机网络的IP ip addr 本地向虚拟机传送文件 scp 文件 rootpath 虚拟机路径 enter后输入密码即可传输&am…

账号列表的删除编辑提交

<template><div><plan title"账号列表"><!-- selection-change"handleSelectionChange"添加这个属性就是点击可以得到你想要的value值 --><el-tablestyle"width: 100%":data"list"selection-change"h…

视频基础知识

1.视频比特率 视频的比特率是指传输过程中单位时间传输的数据量。可以理解为视频的编码采样率。单位是kbps&#xff0c;即每秒千比特。视频比特率是决定视频清晰度的一个重要指标。比特率越高&#xff0c;视频越清晰&#xff0c;但数据量也会越大。比如一部100分钟的电影&#…

K8S初级入门系列之五-Pod的高级特性

一、前言 前一篇我们了解了Pod的基本概念和操作&#xff0c;本篇我们继续研究Pod的一些高级特性&#xff0c;包括Pod的生命周期&#xff0c;pod探针&#xff0c;pod的调度等。 二、生命周期 1、Pod的生命周期 Pod的生命周期示意图如下&#xff1a; 挂起(Pending)&#xff0c…

【C进阶】指针进阶(1)_二次复习版

目录 1. 字符指针 1.1常量字符串的修改 加上const解决问题 打印常量字符串 1.2数组存放的字符串 1.3例题:数组创建与常量池的区别 2. 指针数组 2.1字符指针数组 2.2整型指针数组 2.3使用3个一维数组,模拟实现一个二维数组 2.4例题: 3.数组指针 3.1 数组指针的定义…

老年公寓人员定位管理系统:提升安全与关怀的智能解决方案

老年公寓作为提供安全居住环境和关怀服务的重要场所&#xff0c;面临着人员管理和安全控制的挑战。为了解决这些问题&#xff0c;老年公寓人员定位管理系统应运而生。基于为提供全面的安全管理和个性化关怀服务&#xff0c;华安联大便通过老年公寓人员定位管理系统的技术原理、…

数字孪生和 GIS 系统融合将为水利领域带来哪些变化?

随着科技的不断进步&#xff0c;数字孪生和 GIS 系统的融合应用逐渐成为了水利领域的新趋势。数字孪生是指通过数字化技术模拟物理实体和过程&#xff0c;将现实世界与虚拟世界相结合的技术&#xff0c;而 GIS 系统则是地理信息系统&#xff0c;用于收集、存储、管理和分析地理…

网工内推 | 售前、售后工程师,IE认证优先

01 广州佳杰科技有限公司 招聘岗位&#xff1a;IT售前工程师 职责描述&#xff1a; 1、负责所在区域 IT 产品的售前技术支持工作,包括客户交流、方案编写、配置报价、投标应标、测试、赋能等; 2、与厂商相关人员建立和保持良好的关系,相互配合,提高项目成功率和厂商满意度; 3、…

Python:使用openpyxl读取Excel文件转为json数据

文档 https://openpyxl.readthedocs.io/en/stable/https://pypi.org/project/openpyxl/ 安装 pip install openpyxl环境 $ python --version Python 3.7.0读取文件示例&#xff1a;将Excel文件读取为json数据 有如下一个文件 data.xlsx 实现代码 # -*- coding: utf-8 -…

如何恢复损坏/删除的 Word 文件

有关如何修复不可读的 Microsoft Word 文件或 Rich Text 文件中的文本的分步说明。这些说明有助于从损坏的*.doc、*.docx、*.dot、*.dotx、*.rtf文件&#xff08;任何版本和大小&#xff09;中提取文本&#xff0c;只需单击几下&#xff1a; 从此处下载奇客数据恢复 &#xff…

如何在Linux系统中安装ActiveMQ

1、环境 ActiveMQ是一个纯Java程序&#xff0c;这里安装5.18.2版ActiveMQ&#xff0c;该版MQ运行在JDK 11环境内&#xff0c;为此需要先搭建JDK 11环境&#xff0c;这里安装JDK 15。 1.1、卸载 卸载开源JDK软件包&#xff0c;如下所示&#xff1a; [rootlocalhost ~]# rpm -…

如何将表格中的状态数据转换为Tag标签显示

考虑到系统前端页面的美观程度&#xff0c;通常通过Tag标签来代替某条数据中的状态信息。仅通过一点操作&#xff0c;便能够使得页面美观程度得到较大提升&#xff0c;前后对比如下所示。代码基于Vue以及Element-ui组件实现。 修改前&#xff1a; 修改后&#xff1a; 修改前…

1.Flink概述

1.1 技术架构 应用框架层: 在API层之上构建的满足特定应用场景的计算框架&#xff0c;总体上分为流计算和批处理两类应用框架。API 层&#xff1a; Flink对外提供能力的接口 &#xff0c;实现了面向流计算的DataStream API和面向批处理的DataSet API。运行时层&#xff1a;Flin…

Visio文件编辑查看工具Visio Viewer for Mac

Visio Viewer for Mac可以打开和查看Visio文件&#xff08;.vsd、.vdx和.vsdm文件&#xff09;。它具有简单易用的用户界面&#xff0c;可以快速加载和显示Visio文件。此外&#xff0c;它还支持导出文件为PDF、PNG、JPEG等格式&#xff0c;方便用户进行文件转换和共享。 Visio…

《零基础入门学习Python》第053讲:论一只爬虫的自我修养

0. 请写下这一节课你学习到的内容&#xff1a;格式不限&#xff0c;回忆并复述是加强记忆的好方式&#xff01; 马上我们的教学就要进入最后一个章节&#xff0c;Pygame 嗨爆引爆全场&#xff0c;但由于发生了一个小插曲&#xff0c;所以这里决定追加一个章节&#xff0c;因为…

Qt Core学习日记——第四天QMetaEnum(下)

类定义&#xff1a; 成员变量就只有QMetaObject *mobj和uint handle&#xff0c;handle同样用于计算在qt_meta_stringdata_XTest的位置 成员函数&#xff1a; 接下以test类进行函数讲解 test.h #pragma once #include <qobject.h> #include <QFlags> class X…

有些能力,是工作中学不来的,看看这篇超过90%同行

俗话说:360行&#xff0c;行行转IT。 在就业形势压力巨大的今天&#xff0c;不仅仅是计算机专业的毕业生&#xff0c;很多其他专业的大学生都选择转行从事计算机行业。 尤其是软件测试行业&#xff0c;远远超出其他行业的薪水和广阔的就业前景&#xff0c;吸引了大批应届毕业…

QT DAY1

1.思维导体 2.作业 #include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {qDebug()<<this->size();qDebug()<<this->rect().size();qDebug()<<this->geometry().size();qDebug()<<this->frameGeometry().siz…