- assert 断言
- async_hooks 异步钩子
- async_hooks/context 异步上下文
- buffer 缓冲区
- C++插件
- C/C++插件(使用 Node-API)
- C++嵌入器
- child_process 子进程
- cluster 集群
- CLI 命令行
- console 控制台
- crypto 加密
- crypto/webcrypto 网络加密
- debugger 调试器
- deprecation 弃用
- dgram 数据报
- diagnostics_channel 诊断通道
- dns 域名服务器
- domain 域
- Error 错误
- events 事件触发器
- fs 文件系统
- global 全局变量
- http 超文本传输协议
- http2 超文本传输协议 2.0
- https 安全超文本传输协议
- inspector 检查器
- Intl 国际化
- module 模块
- module/cjs CommonJS 模块
- module/esm ECMAScript 模块
- module/package 包模块
- module/typescript TS 模块
- net 网络
- os 操作系统
- path 路径
- perf_hooks 性能钩子
- permission 权限
- process 进程
- punycode 域名代码
- querystring 查询字符串
- readline 逐行读取
- repl 交互式解释器
- report 诊断报告
- sea 单个可执行应用程序
Node.js v24.13.0 文档
- Node.js v24.13.0
-
目录
- 测试运行器
- 子测试
- 跳过测试
- 重新运行失败的测试
- TODO 测试
describe()和it()别名仅测试- 按名称过滤测试
- 无关的异步活动
- 监视模式
- 全局设置和拆卸
- 从命令行运行测试
- 收集代码覆盖率
- 模拟
- 快照测试
- 测试报告器
run([options])suite([name][, options][, fn])suite.skip([name][, options][, fn])suite.todo([name][, options][, fn])suite.only([name][, options][, fn])test([name][, options][, fn])test.skip([name][, options][, fn])test.todo([name][, options][, fn])test.only([name][, options][, fn])describe([name][, options][, fn])describe.skip([name][, options][, fn])describe.todo([name][, options][, fn])describe.only([name][, options][, fn])it([name][, options][, fn])it.skip([name][, options][, fn])it.todo([name][, options][, fn])it.only([name][, options][, fn])before([fn][, options])after([fn][, options])beforeEach([fn][, options])afterEach([fn][, options])assertsnapshot- 类:
MockFunctionContext - 类:
MockModuleContext - 类:
MockPropertyContext - 类:
MockTrackermock.fn([original[, implementation]][, options])mock.getter(object, methodName[, implementation][, options])mock.method(object, methodName[, implementation][, options])mock.module(specifier[, options])mock.property(object, propertyName[, value])mock.reset()mock.restoreAll()mock.setter(object, methodName[, implementation][, options])
- 类:
MockTimers - 类:
TestsStream - 类:
TestContextcontext.before([fn][, options])context.beforeEach([fn][, options])context.after([fn][, options])context.afterEach([fn][, options])context.assertcontext.diagnostic(message)context.filePathcontext.fullNamecontext.namecontext.plan(count[,options])context.runOnly(shouldRunOnlyTests)context.signalcontext.skip([message])context.todo([message])context.test([name][, options][, fn])context.waitFor(condition[, options])
- 类:
SuiteContext
- 测试运行器
-
导航
- assert 断言
- async_hooks 异步钩子
- async_hooks/context 异步上下文
- buffer 缓冲区
- C++插件
- C/C++插件(使用 Node-API)
- C++嵌入器
- child_process 子进程
- cluster 集群
- CLI 命令行
- console 控制台
- crypto 加密
- crypto/webcrypto 网络加密
- debugger 调试器
- deprecation 弃用
- dgram 数据报
- diagnostics_channel 诊断通道
- dns 域名服务器
- domain 域
- Error 错误
- events 事件触发器
- fs 文件系统
- global 全局变量
- http 超文本传输协议
- http2 超文本传输协议 2.0
- https 安全超文本传输协议
- inspector 检查器
- Intl 国际化
- module 模块
- module/cjs CommonJS 模块
- module/esm ECMAScript 模块
- module/package 包模块
- module/typescript TS 模块
- net 网络
- os 操作系统
- path 路径
- perf_hooks 性能钩子
- permission 权限
- process 进程
- punycode 域名代码
- querystring 查询字符串
- readline 逐行读取
- repl 交互式解释器
- report 诊断报告
- sea 单个可执行应用程序
- 其他版本
测试运行器#>
【Test runner】
源代码: lib/test.js
node:test 模块便于创建 JavaScript 测试。要访问它,请使用:
【The node:test module facilitates the creation of JavaScript tests.
To access it:】
import test from 'node:test';const test = require('node:test');
此模块仅在 node: 方案下可用。
【This module is only available under the node: scheme.】
通过 test 模块创建的测试由单个函数组成,该函数可以通过三种方式之一进行处理:
【Tests created via the test module consist of a single function that is
processed in one of three ways:】
- 一个同步函数,如果抛出异常则被视为失败,否则视为通过。
- 一个返回
Promise的函数,如果Promise被拒绝则被视为失败,如果Promise成功履行则被视为通过。 - 一个接收回调函数的函数。如果回调函数的第一个参数接收到任何真值,则测试视为失败。如果回调函数的第一个参数接收到假值,则测试视为通过。如果测试函数既接收回调函数又返回
Promise,则测试将失败。
以下示例说明了如何使用 test 模块编写测试。
【The following example illustrates how tests are written using the
test module.】
test('synchronous passing test', (t) => {
// This test passes because it does not throw an exception.
assert.strictEqual(1, 1);
});
test('synchronous failing test', (t) => {
// This test fails because it throws an exception.
assert.strictEqual(1, 2);
});
test('asynchronous passing test', async (t) => {
// This test passes because the Promise returned by the async
// function is settled and not rejected.
assert.strictEqual(1, 1);
});
test('asynchronous failing test', async (t) => {
// This test fails because the Promise returned by the async
// function is rejected.
assert.strictEqual(1, 2);
});
test('failing test using Promises', (t) => {
// Promises can be used directly as well.
return new Promise((resolve, reject) => {
setImmediate(() => {
reject(new Error('this will cause the test to fail'));
});
});
});
test('callback passing test', (t, done) => {
// done() is the callback function. When the setImmediate() runs, it invokes
// done() with no arguments.
setImmediate(done);
});
test('callback failing test', (t, done) => {
// When the setImmediate() runs, done() is invoked with an Error object and
// the test fails.
setImmediate(() => {
done(new Error('callback failure'));
});
});
如果有任何测试未通过,进程的退出代码将设置为 1。
【If any tests fail, the process exit code is set to 1.】
子测试#>
【Subtests】
测试上下文的 test() 方法允许创建子测试。它让你可以以层次化的方式组织测试,在一个较大的测试中创建嵌套测试。该方法的行为与顶层的 test() 函数完全相同。以下示例演示了创建一个包含两个子测试的顶层测试。
【The test context's test() method allows subtests to be created.
It allows you to structure your tests in a hierarchical manner,
where you can create nested tests within a larger test.
This method behaves identically to the top level test() function.
The following example demonstrates the creation of a
top level test with two subtests.】
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);
});
});
注意:
beforeEach和afterEach钩子会在每个子测试执行之间触发。
在这个例子中,await 用于确保两个子测试都已完成。这是必要的,因为测试不会等待其子测试完成,不像在测试套件中创建的测试。当父测试完成时,任何仍未完成的子测试将被取消并视为失败。任何子测试的失败都会导致父测试失败。
【In this example, await is used to ensure that both subtests have completed.
This is necessary because tests do not wait for their subtests to
complete, unlike tests created within suites.
Any subtests that are still outstanding when their parent finishes
are cancelled and treated as failures. Any subtest failures cause the parent
test to fail.】
跳过测试#>
【Skipping tests】
可以通过向测试传递 skip 选项来跳过单个测试,或者像下面的示例中那样调用测试上下文的 skip() 方法。
【Individual tests can be skipped by passing the skip option to the test, or by
calling the test context's skip() method as shown in the
following example.】
// The skip option is used, but no message is provided.
test('skip option', { skip: true }, (t) => {
// This code is never executed.
});
// The skip option is used, and a message is provided.
test('skip option with message', { skip: 'this is skipped' }, (t) => {
// This code is never executed.
});
test('skip() method', (t) => {
// Make sure to return here as well if the test contains additional logic.
t.skip();
});
test('skip() method with message', (t) => {
// Make sure to return here as well if the test contains additional logic.
t.skip('this is skipped');
});
重新运行失败的测试#>
【Rerunning failed tests】
The test runner supports persisting the state of the run to a file, allowing
the test runner to rerun failed tests without having to re-run the entire test suite.
Use the --test-rerun-failures command-line option to specify a file path where the
state of the run is stored. if the state file does not exist, the test runner will
create it.
the state file is a JSON file that contains an array of run attempts.
Each run attempt is an object mapping successful tests to the attempt they have passed in.
The key identifying a test in this map is the test file path, with the line and column where the test is defined.
in a case where a test defined in a specific location is run multiple times,
for example within a function or a loop,
a counter will be appended to the key, to disambiguate the test runs.
note changing the order of test execution or the location of a test can lead the test runner
to consider tests as passed on a previous attempt,
meaning --test-rerun-failures should be used when tests run in a deterministic order.
状态文件示例:
【example of a state file:】
[
{
"test.js:10:5": { "passed_on_attempt": 0, "name": "test 1" },
},
{
"test.js:10:5": { "passed_on_attempt": 0, "name": "test 1" },
"test.js:20:5": { "passed_on_attempt": 1, "name": "test 2" }
}
]
在这个例子中,有两次运行尝试,并且在 test.js 中定义了两个测试,第一个测试在第一次尝试时成功,第二个测试在第二次尝试时成功。
【in this example, there are two run attempts, with two tests defined in test.js,
the first test succeeded on the first attempt, and the second test succeeded on the second attempt.】
当使用 --test-rerun-failures 选项时,测试运行器将只运行尚未通过的测试。
【When the --test-rerun-failures option is used, the test runner will only run tests that have not yet passed.】
node --test-rerun-failures /path/to/state/file
TODO 测试#>
【TODO tests】
可以通过向测试传递 todo 选项,或调用测试上下文的 todo() 方法,将单个测试标记为不稳定或未完成,如下面的示例所示。这些测试表示一个待实现或需要修复的错误。TODO 测试会被执行,但不会被视为测试失败,因此不会影响进程退出代码。如果测试同时被标记为 TODO 和跳过,TODO 选项将被忽略。
【Individual tests can be marked as flaky or incomplete by passing the todo
option to the test, or by calling the test context's todo() method, as shown
in the following example. These tests represent a pending implementation or bug
that needs to be fixed. TODO tests are executed, but are not treated as test
failures, and therefore do not affect the process exit code. If a test is marked
as both TODO and skipped, the TODO option is ignored.】
// The todo option is used, but no message is provided.
test('todo option', { todo: true }, (t) => {
// This code is executed, but not treated as a failure.
throw new Error('this does not fail the test');
});
// The todo option is used, and a message is provided.
test('todo option with message', { todo: 'this is a todo test' }, (t) => {
// This code is executed.
});
test('todo() method', (t) => {
t.todo();
});
test('todo() method with message', (t) => {
t.todo('this is a todo test and is not treated as a failure');
throw new Error('this does not fail the test');
});
describe() 和 it() 别名#>
【describe() and it() aliases】
测试套件和测试也可以使用 describe() 和 it() 函数来编写。describe() 是 suite() 的别名,it() 是 test() 的别名。
【Suites and tests can also be written using the describe() and it()
functions. describe() is an alias for suite(), and it() is an
alias for 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';const { describe, it } = require('node:test');
仅 测试#>
【only tests】
如果使用 --test-only 命令行选项启动 Node.js,或者禁用了测试隔离,则可以通过将 only 选项传递给应运行的测试,从而跳过除所选子集之外的所有测试。当设置了 only 选项的测试时,其所有子测试也会运行。
如果测试套件设置了 only 选项,则套件中的所有测试都会运行,除非其子孙测试也设置了 only 选项,在这种情况下只有这些测试会运行。
【If Node.js is started with the --test-only command-line option, or test
isolation is disabled, it is possible to skip all tests except for a selected
subset by passing the only option to the tests that should run. When a test
with the only option is set, all subtests are also run.
If a suite has the only option set, all tests within the suite are run,
unless it has descendants with the only option set, in which case only those
tests are run.】
在 test()/it() 中使用 子测试 时,需要将所有父测试标记为 only 选项,以仅运行所选的测试子集。
【When using subtests within a test()/it(), it is required to mark
all ancestor tests with the only option to run only a
selected subset of tests.】
测试上下文的 runOnly() 方法可以用于在子测试层面实现相同的行为。未执行的测试将从测试运行器输出中省略。
【The test context's runOnly()
method can be used to implement the same behavior at the subtest level. Tests
that are not executed are omitted from the test runner output.】
// Assume Node.js is run with the --test-only command-line option.
// The suite's 'only' option is set, so these tests are run.
test('this test is run', { only: true }, async (t) => {
// Within this test, all subtests are run by default.
await t.test('running subtest');
// The test context can be updated to run subtests with the 'only' option.
t.runOnly(true);
await t.test('this subtest is now skipped');
await t.test('this subtest is run', { only: true });
// Switch the context back to execute all tests.
t.runOnly(false);
await t.test('this subtest is now run');
// Explicitly do not run these tests.
await t.test('skipped subtest 3', { only: false });
await t.test('skipped subtest 4', { skip: true });
});
// The 'only' option is not set, so this test is skipped.
test('this test is not run', () => {
// This code is not run.
throw new Error('fail');
});
describe('a suite', () => {
// The 'only' option is set, so this test is run.
it('this test is run', { only: true }, () => {
// This code is run.
});
it('this test is not run', () => {
// This code is not run.
throw new Error('fail');
});
});
describe.only('a suite', () => {
// The 'only' option is set, so this test is run.
it('this test is run', () => {
// This code is run.
});
it('this test is run', () => {
// This code is run.
});
});
按名称过滤测试#>
【Filtering tests by name】
--test-name-pattern 命令行选项可用于仅运行名称匹配所提供模式的测试,而 --test-skip-pattern 选项可用于跳过名称匹配所提供模式的测试。测试名称模式被解释为 JavaScript 正则表达式。--test-name-pattern 和 --test-skip-pattern 选项可以多次指定,以便运行嵌套测试。对于每个执行的测试,任何相应的测试钩子,例如 beforeEach(),也会运行。未执行的测试将从测试运行输出中省略。
【The --test-name-pattern command-line option can be used to only run
tests whose name matches the provided pattern, and the
--test-skip-pattern option can be used to skip tests whose name
matches the provided pattern. Test name patterns are interpreted as
JavaScript regular expressions. The --test-name-pattern and
--test-skip-pattern options can be specified multiple times in order to run
nested tests. For each test that is executed, any corresponding test hooks,
such as beforeEach(), are also run. Tests that are not executed are omitted
from the test runner output.】
给定以下测试文件,使用 --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" 等)。
【Given the following test file, starting Node.js with the
--test-name-pattern="test [1-3]" option would cause the test runner to execute
test 1, test 2, and test 3. If test 1 did not match the test name
pattern, then its subtests would not execute, despite matching the pattern. The
same set of tests could also be executed by passing --test-name-pattern
multiple times (e.g. --test-name-pattern="test 1",
--test-name-pattern="test 2", etc.).】
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"(或 --test-skip-pattern="/test [4-5]/i")启动 Node.js 会匹配 Test 4 和 Test 5,因为该模式不区分大小写。
【Test name patterns can also be specified using regular expression literals. This
allows regular expression flags to be used. In the previous example, starting
Node.js with --test-name-pattern="/test [4-5]/i" (or --test-skip-pattern="/test [4-5]/i")
would match Test 4 and Test 5 because the pattern is case-insensitive.】
要将单个测试与模式匹配,可以在模式前加上该测试的所有祖级测试名称,用空格分隔,以确保它是唯一的。例如,给定以下测试文件:
【To match a single test with a pattern, you can prefix it with all its ancestor test names separated by space, to ensure it is unique. For example, given the following test file:】
describe('test 1', (t) => {
it('some test');
});
describe('test 2', (t) => {
it('some test');
});
使用 --test-name-pattern="test 1 some test" 启动 Node.js 将仅匹配 test 1 中的 some test。
【Starting Node.js with --test-name-pattern="test 1 some test" would match
only some test in test 1.】
测试名称模式不会更改测试运行程序执行的文件集。
【Test name patterns do not change the set of files that the test runner executes.】
如果同时提供了 --test-name-pattern 和 --test-skip-pattern,测试必须满足两个条件才能执行。
【If both --test-name-pattern and --test-skip-pattern are supplied,
tests must satisfy both requirements in order to be executed.】
无关的异步活动#>
【Extraneous asynchronous activity】
测试函数执行完成后,结果会迅速报告 尽可能保持测试顺序。不过,这也是可能的 测试函数产生异步活动并存续于测试 就是它自己。测试跑者负责处理此类活动,但不会延迟 报告检测结果以配合该情况。
【Once a test function finishes executing, the results are reported as quickly as possible while maintaining the order of the tests. However, it is possible for the test function to generate asynchronous activity that outlives the test itself. The test runner handles this type of activity, but does not delay the reporting of test results in order to accommodate it.】
在下面的示例中,一个测试在仍有两个 setImmediate() 操作未完成的情况下结束。第一个 setImmediate() 尝试创建一个新的子测试。由于父测试已经完成并输出了其结果,新子测试会立即被标记为失败,并随后报告给 <TestsStream>。
【In the following example, a test completes with two setImmediate()
operations still outstanding. The first setImmediate() attempts to create a
new subtest. Because the parent test has already finished and output its
results, the new subtest is immediately marked as failed, and reported later
to the <TestsStream>.】
第二个 setImmediate() 会触发 uncaughtException 事件。来自已完成测试的 uncaughtException 和 unhandledRejection 事件会被 test 模块标记为失败,并由 <TestsStream> 在顶层作为诊断警告报告。
【The second setImmediate() creates an uncaughtException event.
uncaughtException and unhandledRejection events originating from a completed
test are marked as failed by the test module and reported as diagnostic
warnings at the top level by the <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');
});
// The test finishes after this line.
});
监视模式#>
【Watch mode】
Node.js 测试运行器支持通过传递 --watch 标志来以观察模式运行:
【The Node.js test runner supports running in watch mode by passing the --watch flag:】
node --test --watch
在观察模式下,测试运行器会监视测试文件及其依赖的更改。当检测到更改时,测试运行器会重新运行受更改影响的测试。测试运行器会持续运行,直到进程被终止。
【In watch mode, the test runner will watch for changes to test files and their dependencies. When a change is detected, the test runner will rerun the tests affected by the change. The test runner will continue to run until the process is terminated.】
全局设置和拆卸#>
【Global setup and teardown】
测试运行器支持指定一个模块,该模块将在所有测试执行之前进行评估,并可用于为测试设置全局状态或夹具。这对于准备资源或设置多个测试所需的共享状态非常有用。
【The test runner supports specifying a module that will be evaluated before all tests are executed and can be used to setup global state or fixtures for tests. This is useful for preparing resources or setting up shared state that is required by multiple tests.】
此模块可以导出以下任意值:
【This module can export any of the following:】
- 一个在所有测试开始前运行一次的
globalSetup函数 - 一个在所有测试完成后运行一次的
globalTeardown函数
在从命令行运行测试时,可以使用 --test-global-setup 标志来指定该模块。
【The module is specified using the --test-global-setup flag when running tests from the command line.】
// setup-module.js
async function globalSetup() {
// Setup shared resources, state, or environment
console.log('Global setup executed');
// Run servers, create files, prepare databases, etc.
}
async function globalTeardown() {
// Clean up resources, state, or environment
console.log('Global teardown executed');
// Close servers, remove files, disconnect from databases, etc.
}
module.exports = { globalSetup, globalTeardown };// setup-module.mjs
export async function globalSetup() {
// Setup shared resources, state, or environment
console.log('Global setup executed');
// Run servers, create files, prepare databases, etc.
}
export async function globalTeardown() {
// Clean up resources, state, or environment
console.log('Global teardown executed');
// Close servers, remove files, disconnect from databases, etc.
}
如果全局设置函数抛出错误,将不会运行任何测试,并且进程将以非零退出代码退出。在这种情况下,全局拆卸函数将不会被调用。
【If the global setup function throws an error, no tests will be run and the process will exit with a non-zero exit code. The global teardown function will not be called in this case.】
从命令行运行测试#>
【Running tests from the command line】
可以通过在命令行传递 --test 标志来调用 Node.js 测试运行器:
【The Node.js test runner can be invoked from the command line by passing the
--test flag:】
node --test
默认情况下,Node.js 将运行与这些模式匹配的所有文件:
【By default, Node.js will run all files matching these patterns:】
**/*.test.{cjs,mjs,js}**/*-test.{cjs,mjs,js}**/*_test.{cjs,mjs,js}**/test-*.{cjs,mjs,js}**/test.{cjs,mjs,js}**/test/**/*.{cjs,mjs,js}
除非提供 --no-strip-types,否则还会匹配以下附加模式:
【Unless --no-strip-types is supplied, the following
additional patterns are also matched:】
**/*.test.{cts,mts,ts}**/*-test.{cts,mts,ts}**/*_test.{cts,mts,ts}**/test-*.{cts,mts,ts}**/test.{cts,mts,ts}**/test/**/*.{cts,mts,ts}
或者,可以将一个或多个全局模式作为 Node.js 命令的最后参数提供,如下所示。全局模式遵循 glob(7) 的行为。为了防止 shell 扩展导致的跨系统兼容性问题,命令行中的全局模式应使用双引号括起来。
【Alternatively, one or more glob patterns can be provided as the
final argument(s) to the Node.js command, as shown below.
Glob patterns follow the behavior of glob(7).
The glob patterns should be enclosed in double quotes on the command line to
prevent shell expansion, which can reduce portability across systems.】
node --test "**/*.test.js" "**/*.spec.js"
匹配的文件将作为测试文件执行。有关测试文件执行的更多信息,请参见测试运行器执行模型部分。
【Matching files are executed as test files. More information on the test file execution can be found in the test runner execution model section.】
测试运行器执行模型#>
【Test runner execution model】
当启用进程级测试隔离时,每个匹配的测试文件都会在单独的子进程中执行。任何时刻运行的子进程的最大数量由 --test-concurrency 标志控制。如果子进程以 0 的退出代码结束,则测试被视为通过。否则,测试被视为失败。测试文件必须能够由 Node.js 执行,但不要求内部必须使用 node:test 模块。
【When process-level test isolation is enabled, each matching test file is
executed in a separate child process. The maximum number of child processes
running at any time is controlled by the --test-concurrency flag. If the
child process finishes with an exit code of 0, the test is considered passing.
Otherwise, the test is considered to be a failure. Test files must be executable
by Node.js, but are not required to use the node:test module internally.】
每个测试文件都会像普通脚本一样执行。也就是说,如果测试文件本身使用 node:test 来定义测试,那么所有这些测试都会在单个应用线程中执行,而不管 test() 的 concurrency 选项的值是多少。
【Each test file is executed as if it was a regular script. That is, if the test
file itself uses node:test to define tests, all of those tests will be
executed within a single application thread, regardless of the value of the
concurrency option of test().】
当进程级别的测试隔离被禁用时,每个匹配的测试文件都会被导入到测试运行器进程中。一旦所有测试文件都被加载,顶层测试将以单线程的方式执行。由于所有测试文件都在相同的上下文中运行,因此测试之间可能会以在启用隔离时无法出现的方式相互影响。例如,如果某个测试依赖于全局状态,那么该状态可能会被来自其他文件的测试修改。
【When process-level test isolation is disabled, each matching test file is imported into the test runner process. Once all test files have been loaded, the top level tests are executed with a concurrency of one. Because the test files are all run within the same context, it is possible for tests to interact with each other in ways that are not possible when isolation is enabled. For example, if a test relies on global state, it is possible for that state to be modified by a test originating from another file.】
子进程选项继承#>
【Child process option inheritance】
在以进程隔离模式(默认模式)运行测试时,生成的子进程会继承父进程的 Node.js 选项,包括 配置文件 中指定的选项。然而,为了确保测试运行器的正常功能,某些标志会被过滤掉:
【When running tests in process isolation mode (the default), spawned child processes inherit Node.js options from the parent process, including those specified in configuration files. However, certain flags are filtered out to enable proper test runner functionality:】
--test- 为防止递归测试执行而禁用--experimental-test-coverage- 由测试运行器管理--watch- 观看模式在父级处理--experimental-default-config-file- 配置文件加载由父级处理--test-reporter- 报告由父进程管理--test-reporter-destination- 输出目标由父级控制--experimental-config-file- 配置文件路径由父级管理
来自命令行参数、环境变量和配置文件的所有其他 Node.js 选项都会被子进程继承。
【All other Node.js options from command line arguments, environment variables, and configuration files are inherited by the child processes.】
收集代码覆盖率#>
【Collecting code coverage】
当使用 --experimental-test-coverage 命令行标志启动 Node.js 时,会收集代码覆盖率,并在所有测试完成后报告统计信息。如果使用 NODE_V8_COVERAGE 环境变量指定代码覆盖率目录,生成的 V8 覆盖率文件将写入该目录。Node.js 核心模块以及 node_modules/ 目录中的文件默认不会包含在覆盖率报告中。但是,它们可以通过 --test-coverage-include 标志显式包含。默认情况下,所有匹配的测试文件都被排除在覆盖率报告之外。可以使用 --test-coverage-exclude 标志覆盖这些排除。如果启用覆盖率,覆盖率报告将通过 'test:coverage' 事件发送到任何 测试报告器。
【When Node.js is started with the --experimental-test-coverage
command-line flag, code coverage is collected and statistics are reported once
all tests have completed. If the NODE_V8_COVERAGE environment variable is
used to specify a code coverage directory, the generated V8 coverage files are
written to that directory. Node.js core modules and files within
node_modules/ directories are, by default, not included in the coverage report.
However, they can be explicitly included via the --test-coverage-include flag.
By default all the matching test files are excluded from the coverage report.
Exclusions can be overridden by using the --test-coverage-exclude flag.
If coverage is enabled, the coverage report is sent to any test reporters via
the 'test:coverage' event.】
可以使用以下注释语法在一系列行上禁用覆盖:
【Coverage can be disabled on a series of lines using the following comment syntax:】
/* node:coverage disable */
if (anAlwaysFalseCondition) {
// Code in this branch will never be executed, but the lines are ignored for
// coverage purposes. All lines following the 'disable' comment are ignored
// until a corresponding 'enable' comment is encountered.
console.log('this is never executed');
}
/* node:coverage enable */
覆盖也可以针对指定行数禁用。在指定行数之后,覆盖将自动重新启用。如果没有明确提供行数,则仅忽略一行。
【Coverage can also be disabled for a specified number of lines. After the specified number of lines, coverage will be automatically reenabled. If the number of lines is not explicitly provided, a single line is ignored.】
/* node:coverage ignore next */
if (anAlwaysFalseCondition) { console.log('this is never executed'); }
/* node:coverage ignore next 3 */
if (anAlwaysFalseCondition) {
console.log('this is never executed');
}
覆盖报告器#>
【Coverage reporters】
tap 和 spec 报告器将打印覆盖率统计摘要。还有一个 lcov 报告器,它将生成一个 lcov 文件,可用于作为详细的覆盖率报告。
【The tap and spec reporters will print a summary of the coverage statistics. There is also an lcov reporter that will generate an lcov file which can be used as an in depth coverage report.】
node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=lcov.info
- 该报告者未报告任何测试结果。
- 理想情况下,该报告者应与其他报告者一起使用。
模拟#>
【Mocking】
node:test 模块在测试期间通过一个顶层的 mock 对象支持模拟。下面的示例创建了一个用于两个数字相加的函数的间谍。然后使用该间谍来断言函数是否按预期被调用。
【The node:test module supports mocking during testing via a top-level mock
object. The following example creates a spy on a function that adds two numbers
together. The spy is then used to assert that the function was called as
expected.】
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.callCount(), 0);
assert.strictEqual(sum(3, 4), 7);
assert.strictEqual(sum.mock.callCount(), 1);
const call = sum.mock.calls[0];
assert.deepStrictEqual(call.arguments, [3, 4]);
assert.strictEqual(call.result, 7);
assert.strictEqual(call.error, undefined);
// Reset the globally tracked mocks.
mock.reset();
});'use strict';
const assert = require('node:assert');
const { mock, test } = require('node:test');
test('spies on a function', () => {
const sum = mock.fn((a, b) => {
return a + b;
});
assert.strictEqual(sum.mock.callCount(), 0);
assert.strictEqual(sum(3, 4), 7);
assert.strictEqual(sum.mock.callCount(), 1);
const call = sum.mock.calls[0];
assert.deepStrictEqual(call.arguments, [3, 4]);
assert.strictEqual(call.result, 7);
assert.strictEqual(call.error, undefined);
// Reset the globally tracked mocks.
mock.reset();
});
相同的模拟功能也在每个测试的 TestContext 对象上公开。以下示例使用 TestContext 上公开的 API 创建了对象方法的间谍。通过测试上下文进行模拟的好处是,测试运行结束后,测试运行器会自动恢复所有被模拟的功能。
【The same mocking functionality is also exposed on the TestContext object
of each test. The following example creates a spy on an object method using the
API exposed on the TestContext. The benefit of mocking via the test context is
that the test runner will automatically restore all mocked functionality once
the test finishes.】
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.callCount(), 0);
assert.strictEqual(number.add(3), 8);
assert.strictEqual(number.add.mock.callCount(), 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,而无需实际等待指定的时间间隔。
【Mocking timers is a technique commonly used in software testing to simulate and
control the behavior of timers, such as setInterval and setTimeout,
without actually waiting for the specified time intervals.】
请参阅 MockTimers 类以获取完整的方法和功能列表。
【Refer to the MockTimers class for a full list of methods and features.】
这允许开发者为时间相关的功能编写更可靠、更可预测的测试。
【This allows developers to write more reliable and predictable tests for time-dependent functionality.】
下面的示例显示了如何模拟 setTimeout。使用 .enable({ apis: ['setTimeout'] }); 它将模拟 node:timers 和 node:timers/promises 模块中的 setTimeout 函数,以及来自 Node.js 全局上下文的 setTimeout。
【The example below shows how to mock setTimeout.
Using .enable({ apis: ['setTimeout'] });
it will mock the setTimeout functions in the node:timers and
node:timers/promises modules,
as well as from the Node.js global context.】
注意: 像 import { setTimeout } from 'node:timers' 这样的解构函数目前不被此 API 支持。
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();
// Optionally choose what to mock
mock.timers.enable({ apis: ['setTimeout'] });
setTimeout(fn, 9999);
assert.strictEqual(fn.mock.callCount(), 0);
// Advance in time
mock.timers.tick(9999);
assert.strictEqual(fn.mock.callCount(), 1);
// Reset the globally tracked mocks.
mock.timers.reset();
// If you call reset mock instance, it will also reset timers instance
mock.reset();
});const assert = require('node:assert');
const { mock, test } = require('node:test');
test('mocks setTimeout to be executed synchronously without having to actually wait for it', () => {
const fn = mock.fn();
// Optionally choose what to mock
mock.timers.enable({ apis: ['setTimeout'] });
setTimeout(fn, 9999);
assert.strictEqual(fn.mock.callCount(), 0);
// Advance in time
mock.timers.tick(9999);
assert.strictEqual(fn.mock.callCount(), 1);
// Reset the globally tracked mocks.
mock.timers.reset();
// If you call reset mock instance, it will also reset timers instance
mock.reset();
});
相同的模拟功能也可以通过每个测试的 TestContext 对象上的 mock 属性访问。通过测试上下文进行模拟的好处是,一旦测试结束,测试运行器会自动恢复所有被模拟的定时器功能。
【The same mocking functionality is also exposed in the mock property on the TestContext object
of each test. The benefit of mocking via the test context is
that the test runner will automatically restore all mocked timers
functionality once the test finishes.】
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();
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['setTimeout'] });
setTimeout(fn, 9999);
assert.strictEqual(fn.mock.callCount(), 0);
// Advance in time
context.mock.timers.tick(9999);
assert.strictEqual(fn.mock.callCount(), 1);
});const assert = require('node:assert');
const { test } = require('node:test');
test('mocks setTimeout to be executed synchronously without having to actually wait for it', (context) => {
const fn = context.mock.fn();
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['setTimeout'] });
setTimeout(fn, 9999);
assert.strictEqual(fn.mock.callCount(), 0);
// Advance in time
context.mock.timers.tick(9999);
assert.strictEqual(fn.mock.callCount(), 1);
});
日期#>
【Dates】
模拟计时器 API 还允许对 Date 对象进行模拟。这是一个用于测试依赖时间功能或模拟内部日历函数(如 Date.now())的有用功能。
【The mock timers API also allows the mocking of the Date object. This is a
useful feature for testing time-dependent functionality, or to simulate
internal calendar functions such as Date.now().】
日期的实现也是 MockTimers 类的一部分。有关方法和功能的完整列表,请参阅它。
【The dates implementation is also part of the MockTimers class. Refer to it
for a full list of methods and features.】
注意: 当同时模拟日期和定时器时,它们是相互依赖的。这意味着如果你同时模拟了 Date 和 setTimeout,时间的推进也会同时推进模拟的日期,因为它们模拟的是同一个内部时钟。
下面的示例展示了如何模拟 Date 对象并获取当前的 Date.now() 值。
【The example below show how to mock the Date object and obtain the current
Date.now() value.】
import assert from 'node:assert';
import { test } from 'node:test';
test('mocks the Date object', (context) => {
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['Date'] });
// If not specified, the initial date will be based on 0 in the UNIX epoch
assert.strictEqual(Date.now(), 0);
// Advance in time will also advance the date
context.mock.timers.tick(9999);
assert.strictEqual(Date.now(), 9999);
});const assert = require('node:assert');
const { test } = require('node:test');
test('mocks the Date object', (context) => {
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['Date'] });
// If not specified, the initial date will be based on 0 in the UNIX epoch
assert.strictEqual(Date.now(), 0);
// Advance in time will also advance the date
context.mock.timers.tick(9999);
assert.strictEqual(Date.now(), 9999);
});
如果没有设置初始纪元,初始日期将基于 Unix 纪元中的 0。即 1970 年 1 月 1 日 00:00:00 UTC。你可以通过向 .enable() 方法传递 now 属性来设置初始日期。此值将用作模拟 Date 对象的初始日期。它可以是正整数,也可以是另一个 Date 对象。
【If there is no initial epoch set, the initial date will be based on 0 in the
Unix epoch. This is January 1st, 1970, 00:00:00 UTC. You can set an initial date
by passing a now property to the .enable() method. This value will be used
as the initial date for the mocked Date object. It can either be a positive
integer, or another Date object.】
import assert from 'node:assert';
import { test } from 'node:test';
test('mocks the Date object with initial time', (context) => {
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['Date'], now: 100 });
assert.strictEqual(Date.now(), 100);
// Advance in time will also advance the date
context.mock.timers.tick(200);
assert.strictEqual(Date.now(), 300);
});const assert = require('node:assert');
const { test } = require('node:test');
test('mocks the Date object with initial time', (context) => {
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['Date'], now: 100 });
assert.strictEqual(Date.now(), 100);
// Advance in time will also advance the date
context.mock.timers.tick(200);
assert.strictEqual(Date.now(), 300);
});
你可以使用 .setTime() 方法手动将模拟的日期移动到另一个时间。此方法只接受正整数。
【You can use the .setTime() method to manually move the mocked date to another
time. This method only accepts a positive integer.】
注意: 该方法会执行从新时间开始已经过去的所有模拟计时器。
在下面的示例中,我们为模拟日期设置新时间。
【In the below example we are setting a new time for the mocked date.】
import assert from 'node:assert';
import { test } from 'node:test';
test('sets the time of a date object', (context) => {
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['Date'], now: 100 });
assert.strictEqual(Date.now(), 100);
// Advance in time will also advance the date
context.mock.timers.setTime(1000);
context.mock.timers.tick(200);
assert.strictEqual(Date.now(), 1200);
});const assert = require('node:assert');
const { test } = require('node:test');
test('sets the time of a date object', (context) => {
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['Date'], now: 100 });
assert.strictEqual(Date.now(), 100);
// Advance in time will also advance the date
context.mock.timers.setTime(1000);
context.mock.timers.tick(200);
assert.strictEqual(Date.now(), 1200);
});
如果你有任何设置为在过去运行的计时器,它将被执行,就好像调用了 .tick() 方法一样。如果你想测试已经发生的依赖时间的功能,这非常有用。
【If you have any timer that's set to run in the past, it will be executed as if
the .tick() method has been called. This is useful if you want to test
time-dependent functionality that's already in the past.】
import assert from 'node:assert';
import { test } from 'node:test';
test('runs timers as setTime passes ticks', (context) => {
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
const fn = context.mock.fn();
setTimeout(fn, 1000);
context.mock.timers.setTime(800);
// Timer is not executed as the time is not yet reached
assert.strictEqual(fn.mock.callCount(), 0);
assert.strictEqual(Date.now(), 800);
context.mock.timers.setTime(1200);
// Timer is executed as the time is now reached
assert.strictEqual(fn.mock.callCount(), 1);
assert.strictEqual(Date.now(), 1200);
});const assert = require('node:assert');
const { test } = require('node:test');
test('runs timers as setTime passes ticks', (context) => {
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
const fn = context.mock.fn();
setTimeout(fn, 1000);
context.mock.timers.setTime(800);
// Timer is not executed as the time is not yet reached
assert.strictEqual(fn.mock.callCount(), 0);
assert.strictEqual(Date.now(), 800);
context.mock.timers.setTime(1200);
// Timer is executed as the time is now reached
assert.strictEqual(fn.mock.callCount(), 1);
assert.strictEqual(Date.now(), 1200);
});
使用 .runAll() 将会执行当前队列中的所有定时器。这也会将模拟的日期推进到最后执行的定时器的时间,就好像时间已经过去了一样。
【Using .runAll() will execute all timers that are currently in the queue. This
will also advance the mocked date to the time of the last timer that was
executed as if the time has passed.】
import assert from 'node:assert';
import { test } from 'node:test';
test('runs timers as setTime passes ticks', (context) => {
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
const fn = context.mock.fn();
setTimeout(fn, 1000);
setTimeout(fn, 2000);
setTimeout(fn, 3000);
context.mock.timers.runAll();
// All timers are executed as the time is now reached
assert.strictEqual(fn.mock.callCount(), 3);
assert.strictEqual(Date.now(), 3000);
});const assert = require('node:assert');
const { test } = require('node:test');
test('runs timers as setTime passes ticks', (context) => {
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
const fn = context.mock.fn();
setTimeout(fn, 1000);
setTimeout(fn, 2000);
setTimeout(fn, 3000);
context.mock.timers.runAll();
// All timers are executed as the time is now reached
assert.strictEqual(fn.mock.callCount(), 3);
assert.strictEqual(Date.now(), 3000);
});
快照测试#>
【Snapshot testing】
快照测试允许将任意值序列化为字符串值,并与一组已知的良好值进行比较。已知的良好值被称为快照,并存储在快照文件中。快照文件由测试运行器管理,但设计为可供人类阅读,以便调试。最佳做法是将快照文件与测试文件一起提交到源代码管理中。
【Snapshot tests allow arbitrary values to be serialized into string values and compared against a set of known good values. The known good values are known as snapshots, and are stored in a snapshot file. Snapshot files are managed by the test runner, but are designed to be human readable to aid in debugging. Best practice is for snapshot files to be checked into source control along with your test files.】
快照文件是通过使用 --test-update-snapshots 命令行标志启动 Node.js 时生成的。每个测试文件都会生成一个单独的快照文件。默认情况下,快照文件的名称与测试文件相同,并且使用 .snapshot 文件扩展名。此行为可以通过 snapshot.setResolveSnapshotPath() 函数进行配置。每个快照断言对应快照文件中的一个导出项。
【Snapshot files are generated by starting Node.js with the
--test-update-snapshots command-line flag. A separate snapshot file is
generated for each test file. By default, the snapshot file has the same name
as the test file with a .snapshot file extension. This behavior can be
configured using the snapshot.setResolveSnapshotPath() function. Each
snapshot assertion corresponds to an export in the snapshot file.】
下面展示了一个快照测试的示例。第一次执行此测试时,它会失败,因为相应的快照文件不存在。
【An example snapshot test is shown below. The first time this test is executed, it will fail because the corresponding snapshot file does not exist.】
// test.js
suite('suite of snapshot tests', () => {
test('snapshot test', (t) => {
t.assert.snapshot({ value1: 1, value2: 2 });
t.assert.snapshot(5);
});
});
通过运行带有 --test-update-snapshots 的测试文件来生成快照文件。测试应该通过,并且在与测试文件相同的目录下会创建一个名为 test.js.snapshot 的文件。快照文件的内容如下所示。每个快照由测试的全名和一个计数器标识,用于区分同一测试中的不同快照。
【Generate the snapshot file by running the test file with
--test-update-snapshots. The test should pass, and a file named
test.js.snapshot is created in the same directory as the test file. The
contents of the snapshot file are shown below. Each snapshot is identified by
the full name of test and a counter to differentiate between snapshots in the
same test.】
exports[`suite of snapshot tests > snapshot test 1`] = `
{
"value1": 1,
"value2": 2
}
`;
exports[`suite of snapshot tests > snapshot test 2`] = `
5
`;
一旦快照文件创建完成,再次运行测试,不要使用 --test-update-snapshots 标志。现在测试应该可以通过了。
【Once the snapshot file is created, run the tests again without the
--test-update-snapshots flag. The tests should pass now.】
测试报告器#>
【Test reporters】
node:test 模块支持传递 --test-reporter 标志,以便测试运行器使用特定的报告器。
【The node:test module supports passing --test-reporter
flags for the test runner to use a specific reporter.】
支持以下内置报告器:
【The following built-reporters are supported:】
-
specspec报告器以人类可读的格式输出测试结果。 这是默认的报告器。 -
taptap报告器以 轻拍 格式输出测试结果。 -
dotdot报告器以紧凑的格式输出测试结果, 每个通过的测试用.表示, 每个失败的测试用X表示。 -
junitjunit 报告器以 jUnit XML 格式输出测试结果 -
lcov当与--experimental-test-coverage标志一起使用时,lcov报告器会输出测试覆盖率。
这些报告器的具体输出可能会因 Node.js 的版本而变化,因此不应在程序中依赖。如果需要以编程方式访问测试运行器的输出,请使用 <TestsStream> 发出的事件。
【The exact output of these reporters is subject to change between versions of Node.js, and should not be relied on programmatically. If programmatic access to the test runner's output is required, use the events emitted by the <TestsStream>.】
可以通过 node:test/reporters 模块使用这些 reporters:
【The reporters are available via the node:test/reporters module:】
import { tap, spec, dot, junit, lcov } from 'node:test/reporters';const { tap, spec, dot, junit, lcov } = require('node:test/reporters');
自定义报告器#>
【Custom reporters】
--test-reporter 可用于指定自定义报告器的路径。自定义报告器是一个模块,它导出一个 流.组合 可接受的值。报告器应转换由 <TestsStream> 发出的事件。
使用 <stream.Transform> 的自定义报告示例:
【Example of a custom reporter using <stream.Transform>:】
import { Transform } from 'node:stream';
const customReporter = new Transform({
writableObjectMode: true,
transform(event, encoding, callback) {
switch (event.type) {
case 'test:dequeue':
callback(null, `test ${event.data.name} dequeued`);
break;
case 'test:enqueue':
callback(null, `test ${event.data.name} enqueued`);
break;
case 'test:watch:drained':
callback(null, 'test watch queue drained');
break;
case 'test:watch:restarted':
callback(null, 'test watch restarted due to file change');
break;
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':
case 'test:stderr':
case 'test:stdout':
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;const { Transform } = require('node:stream');
const customReporter = new Transform({
writableObjectMode: true,
transform(event, encoding, callback) {
switch (event.type) {
case 'test:dequeue':
callback(null, `test ${event.data.name} dequeued`);
break;
case 'test:enqueue':
callback(null, `test ${event.data.name} enqueued`);
break;
case 'test:watch:drained':
callback(null, 'test watch queue drained');
break;
case 'test:watch:restarted':
callback(null, 'test watch restarted due to file change');
break;
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':
case 'test:stderr':
case 'test:stdout':
callback(null, event.data.message);
break;
case 'test:coverage': {
const { totalLineCount } = event.data.summary.totals;
callback(null, `total line count: ${totalLineCount}\n`);
break;
}
}
},
});
module.exports = customReporter;
使用生成器函数的自定义报告器示例:
【Example of a custom reporter using a generator function:】
export default async function * customReporter(source) {
for await (const event of source) {
switch (event.type) {
case 'test:dequeue':
yield `test ${event.data.name} dequeued\n`;
break;
case 'test:enqueue':
yield `test ${event.data.name} enqueued\n`;
break;
case 'test:watch:drained':
yield 'test watch queue drained\n';
break;
case 'test:watch:restarted':
yield 'test watch restarted due to file change\n';
break;
case 'test:start':
yield `test ${event.data.name} started\n`;
break;
case 'test:pass':
yield `test ${event.data.name} passed\n`;
break;
case 'test:fail':
yield `test ${event.data.name} failed\n`;
break;
case 'test:plan':
yield 'test plan\n';
break;
case 'test:diagnostic':
case 'test:stderr':
case 'test:stdout':
yield `${event.data.message}\n`;
break;
case 'test:coverage': {
const { totalLineCount } = event.data.summary.totals;
yield `total line count: ${totalLineCount}\n`;
break;
}
}
}
}module.exports = async function * customReporter(source) {
for await (const event of source) {
switch (event.type) {
case 'test:dequeue':
yield `test ${event.data.name} dequeued\n`;
break;
case 'test:enqueue':
yield `test ${event.data.name} enqueued\n`;
break;
case 'test:watch:drained':
yield 'test watch queue drained\n';
break;
case 'test:watch:restarted':
yield 'test watch restarted due to file change\n';
break;
case 'test:start':
yield `test ${event.data.name} started\n`;
break;
case 'test:pass':
yield `test ${event.data.name} passed\n`;
break;
case 'test:fail':
yield `test ${event.data.name} failed\n`;
break;
case 'test:plan':
yield 'test plan\n';
break;
case 'test:diagnostic':
case 'test:stderr':
case 'test:stdout':
yield `${event.data.message}\n`;
break;
case 'test:coverage': {
const { totalLineCount } = event.data.summary.totals;
yield `total line count: ${totalLineCount}\n`;
break;
}
}
}
};
--test-reporter 提供的值应该是一个字符串,就像在 JavaScript 代码中的 import() 使用的那样,或者是为 --import 提供的值。
【The value provided to --test-reporter should be a string like one used in an
import() in JavaScript code, or a value provided for --import.】
多报告器#>
【Multiple reporters】
--test-reporter 标志可以多次指定,以便以多种格式报告测试结果。在这种情况下,需要使用 --test-reporter-destination 为每个报告器指定一个目标。目标可以是 stdout、stderr 或文件路径。报告器和目标将按照它们被指定的顺序进行配对。
【The --test-reporter flag can be specified multiple times to report test
results in several formats. In this situation
it is required to specify a destination for each reporter
using --test-reporter-destination.
Destination can be stdout, stderr, or a file path.
Reporters and destinations are paired according
to the order they were specified.】
在下面的示例中,spec 报告器将输出到 stdout,而 dot 报告器将输出到 file.txt:
【In the following example, the spec reporter will output to stdout,
and the dot reporter will output to file.txt:】
node --test-reporter=spec --test-reporter=dot --test-reporter-destination=stdout --test-reporter-destination=file.txt
当只指定一个报告器时,输出目标将默认为 stdout,除非明确提供了输出目标。
【When a single reporter is specified, the destination will default to stdout,
unless a destination is explicitly provided.】
run([options])#>
options<Object> 运行测试的配置选项。支持以下属性:concurrency<number> | <boolean> 如果提供了一个数字,则会同时运行那么多测试进程,每个进程对应一个测试文件。如果为true,则会同时运行os.availableParallelism() - 1个测试文件。如果为false,则每次只运行一个测试文件。默认值:false。cwd<string> 指定测试运行器要使用的当前工作目录。
作为从该目录解析文件的基路径,就像 从命令行运行测试 一样。
默认值:process.cwd()。files<Array> 包含要运行文件列表的数组。 默认值: 与 从命令行运行测试 相同。- 'forceExit' <boolean> 配置测试运行器退出进程一次 所有已知的测试都已完成,即使事件循环会 否则保持活跃。默认: 'false'。
globPatterns<Array> 包含要匹配测试文件的全局模式列表的数组。此选项不能与files一起使用。默认值: 与 从命令行运行测试 相同。inspectPort<number> | <Function> 设置测试子进程的调试端口。
这可以是一个数字,或者是一个不接受任何参数且返回数字的函数。如果提供了空值,则每个进程将获得自己的端口,从主进程的process.debugPort递增。如果isolation选项设置为'none',则忽略此选项,因为不会生成子进程。默认值:undefined。isolation<string> 配置测试隔离的类型。如果设置为'process',每个测试文件将在单独的子进程中运行。如果设置为'none',所有测试文件将在当前进程中运行。默认值:'process'。only<boolean> 如果为真,测试上下文将只运行设置了only选项的测试setup<Function> 一个接受TestsStream实例的函数,可用于在任何测试运行之前设置监听器。 默认值:undefined。execArgv<Array> 一个用于传递给node可执行文件的命令行标志数组,当启动子进程时使用。 当isolation为'none'时,此选项无效。 默认值:[]argv<Array> 一个数组,包含在生成子进程时传递给每个测试文件的 CLI 标志。当isolation为'none'时,此选项无效。默认值:[]。signal<AbortSignal> 允许中止正在进行的测试执行。testNamePatterns<string> | <RegExp> | <Array> 一个字符串、正则表达式或正则表达式数组,
可用于仅运行名称匹配所提供模式的测试。
测试名称模式被解释为 JavaScript 正则表达式。
对于每个执行的测试,任何相应的测试钩子(如beforeEach())也会被执行。
默认值:undefined。testSkipPatterns<string> | <RegExp> | <Array> 一个字符串、正则表达式或正则表达式数组,
可用于排除名称匹配提供模式的测试。
测试名称模式将被解释为 JavaScript 正则表达式。
对于每个执行的测试,任何相应的测试钩子(例如beforeEach())也会被执行。
默认值:undefined。timeout<number> 测试执行将在指定的毫秒数后失败。
如果未指定,子测试将继承其父测试的此值。
默认值:Infinity。watch<boolean> 是否以监听模式运行。默认值:false。shard<Object> 在特定分片中运行测试。默认值:undefined。- 'rerunFailuresFilePath' <string> 测试运行者将 存储测试状态,以便下次运行时只重跑失败的测试。 更多信息请参见[重跑失败测试][]。 默认: “未定义”。
coverage<boolean> 启用 代码覆盖率 收集。 默认值:false。coverageExcludeGlobs<string> | <Array> 使用 glob 模式排除特定文件的代码覆盖率,该模式可以匹配绝对路径和相对路径的文件。此属性仅在coverage设置为true时适用。如果同时提供coverageExcludeGlobs和coverageIncludeGlobs,文件必须同时满足 两个 条件才能包含在覆盖率报告中。默认值:undefined。coverageIncludeGlobs<string> | <Array> 使用通配符模式包括代码覆盖范围中的特定文件,该模式可以匹配绝对路径和相对路径。此属性仅在coverage设置为true时适用。如果同时提供了coverageExcludeGlobs和coverageIncludeGlobs,文件必须符合 两个 条件才能被包含在覆盖报告中。默认值:undefined。lineCoverage<number> 要求覆盖行的最小百分比。如果代码覆盖率未达到指定的阈值,进程将以代码1退出。 默认值:0。branchCoverage<number> 要求覆盖的分支至少达到一定百分比。如果代码覆盖率未达到指定的阈值,进程将以代码1退出。 默认值:0。functionCoverage<number> 要求覆盖函数的最小百分比。如果代码覆盖率未达到指定的阈值,进程将以代码1退出。 默认值:0。
- 返回:<TestsStream>
注意: shard 用于在多台机器或多个进程之间水平并行运行测试,非常适合在各种环境下进行大规模执行。它与 watch 模式不兼容,后者用于快速代码迭代,通过在文件更改时自动重新运行测试。
import { tap } from 'node:test/reporters';
import { run } from 'node:test';
import process from 'node:process';
import path from 'node:path';
run({ files: [path.resolve('./tests/test.js')] })
.on('test:fail', () => {
process.exitCode = 1;
})
.compose(tap)
.pipe(process.stdout);const { tap } = require('node:test/reporters');
const { run } = require('node:test');
const path = require('node:path');
run({ files: [path.resolve('./tests/test.js')] })
.on('test:fail', () => {
process.exitCode = 1;
})
.compose(tap)
.pipe(process.stdout);
suite([name][, options][, fn])#>
name<string> 套件的名称,在报告测试结果时显示。默认值:fn的name属性,如果fn没有名称则为'<anonymous>'。options<Object> 套件的可选配置选项。支持与test([名称][, options][, fn])相同的选项。fn<Function> | <AsyncFunction> 声明嵌套测试和套件的套件函数。该函数的第一个参数是一个SuiteContext对象。默认值: 空操作函数。- 返回值: <Promise> 立即以
undefined完成。
suite() 函数是从 node:test 模块导入的。
【The suite() function is imported from the node:test module.】
suite.skip([name][, options][, fn])#>
跳过测试套件的简写。这与 suite([name], { skip: true }[, fn]) 相同。
【Shorthand for skipping a suite. This is the same as
suite([name], { skip: true }[, fn]).】
suite.todo([name][, options][, fn])#>
用于将测试套件标记为 TODO 的简写。这与 suite([name], { todo: true }[, fn]) 相同。
【Shorthand for marking a suite as TODO. This is the same as
suite([name], { todo: true }[, fn]).】
suite.only([name][, options][, fn])#>
用于将测试套件标记为 only 的简写。这与 suite([name], { only: true }[, fn]) 相同。
【Shorthand for marking a suite as only. This is the same as
suite([name], { only: true }[, fn]).】
test([name][, options][, fn])#>
name<string> 测试的名称,在报告测试结果时显示。默认值:fn的name属性,如果fn没有名称,则为'<anonymous>'。options<Object> 测试的配置选项。支持以下属性:concurrency<number> | <boolean> 如果提供了一个数字,那么在应用线程中将会有相应数量的测试并行运行。如果为true,所有计划的异步测试将在该线程中同时运行。如果为false,一次只运行一个测试。如果未指定,子测试将继承其父测试的该值。默认值:false。only<boolean> 如果为真,并且测试环境配置为只运行only测试,则将运行此测试。否则,该测试将被跳过。默认值:false。signal<AbortSignal> 允许中止正在进行的测试。skip<boolean> | <string> 如果为真,则跳过该测试。如果提供了字符串,该字符串将显示在测试结果中作为跳过测试的原因。默认值:false。todo<boolean> | <string> 如果为真值,则将测试标记为TODO。如果提供了字符串,则该字符串将在测试结果中显示,作为测试为TODO的原因。默认值:false。timeout<number> 测试将在指定毫秒数后失败。如果未指定,子测试将继承父测试的此值。 默认值:Infinity。plan<number> 预期在测试中运行的断言和子测试的数量。如果测试中运行的断言数量与计划中指定的数量不符,测试将会失败。默认值:undefined。
fn<Function> | <AsyncFunction> 被测试的函数。该函数的第一个参数是一个TestContext对象。如果测试使用回调函数,回调函数作为第二个参数传入。**默认值:**一个空操作函数。- 返回值:<Promise> 在测试完成后,或如果测试在测试套件中运行则立即,fulfilled 为
undefined。
test() 函数是从 test 模块导入的值。每次调用此函数都会将测试结果报告给 <TestsStream>。
【The test() function is the value imported from the test module. Each
invocation of this function results in reporting the test to the <TestsStream>.】
传递给 fn 参数的 TestContext 对象可以用于执行与当前测试相关的操作。示例包括跳过测试、添加额外的诊断信息或创建子测试。
【The TestContext object passed to the fn argument can be used to perform
actions related to the current test. Examples include skipping the test, adding
additional diagnostic information, or creating subtests.】
test() 会返回一个在测试完成后执行的 Promise。
如果在一个测试套件中调用 test(),它会立即完成。
对于顶层测试,通常可以忽略返回值。
然而,对于子测试,应该使用返回值以防止父测试先结束并取消子测试,
如下例所示。
test('top level test', async (t) => {
// The setTimeout() in the following subtest would cause it to outlive its
// parent test if 'await' is removed on the next line. Once the parent test
// completes, it will cancel any outstanding subtests.
await t.test('longer running subtest', async (t) => {
return new Promise((resolve, reject) => {
setTimeout(resolve, 1000);
});
});
});
timeout 选项可用于在测试执行时间超过 timeout 毫秒时使其失败。然而,它并不是一个可靠的取消测试的机制,因为正在运行的测试可能会阻塞应用线程,从而阻止计划中的取消操作。
【The timeout option can be used to fail the test if it takes longer than
timeout milliseconds to complete. However, it is not a reliable mechanism for
canceling tests because a running test might block the application thread and
thus prevent the scheduled cancellation.】
test.skip([name][, options][, fn])#>
跳过测试的简写,与 test([name], { skip: true }[, fn]) 相同。
【Shorthand for skipping a test,
same as test([name], { skip: true }[, fn]).】
test.todo([name][, options][, fn])#>
用于将测试标记为 TODO 的简写,等同于 test([name], { todo: true }[, fn])。
【Shorthand for marking a test as TODO,
same as test([name], { todo: true }[, fn]).】
test.only([name][, options][, fn])#>
用于将测试标记为 only 的简写,等同于 test([name], { only: true }[, fn])。
【Shorthand for marking a test as only,
same as test([name], { only: true }[, fn]).】
describe([name][, options][, fn])#>
suite() 的别名。
【Alias for suite().】
describe() 函数是从 node:test 模块导入的。
【The describe() function is imported from the node:test module.】
describe.skip([name][, options][, fn])#>
跳过测试套件的简写。这与 describe([name], { skip: true }[, fn]) 相同。
【Shorthand for skipping a suite. This is the same as
describe([name], { skip: true }[, fn]).】
describe.todo([name][, options][, fn])#>
用于将测试套件标记为 TODO 的简写。这与 describe([name], { todo: true }[, fn]) 相同。
【Shorthand for marking a suite as TODO. This is the same as
describe([name], { todo: true }[, fn]).】
describe.only([name][, options][, fn])#>
用于将测试套件标记为 only 的简写。这与 describe([name], { only: true }[, fn]) 相同。
【Shorthand for marking a suite as only. This is the same as
describe([name], { only: true }[, fn]).】
it([name][, options][, fn])#>
test() 的别名。
【Alias for test().】
it() 函数是从 node:test 模块导入的。
【The it() function is imported from the node:test module.】
it.skip([name][, options][, fn])#>
跳过测试的简写,与 it([name], { skip: true }[, fn]) 相同。
【Shorthand for skipping a test,
same as it([name], { skip: true }[, fn]).】
it.todo([name][, options][, fn])#>
用于将测试标记为 TODO 的简写,和 it([name], { todo: true }[, fn]) 相同。
【Shorthand for marking a test as TODO,
same as it([name], { todo: true }[, fn]).】
it.only([name][, options][, fn])#>
用于将测试标记为 only 的简写,等同于 it([name], { only: true }[, fn])。
【Shorthand for marking a test as only,
same as it([name], { only: true }[, fn]).】
before([fn][, options])#>
fn<Function> | <AsyncFunction> 钩子函数。如果钩子使用回调,则回调函数作为第二个参数传入。默认值: 一个空操作函数。options<Object> 钩子的配置选项。支持以下属性:signal<AbortSignal> 允许中止正在进行的钩子。timeout<number> 钩子在毫秒数到达后失败。如果未指定,子测试将继承父测试的该值。默认值:Infinity。
该函数创建一个在执行套件之前运行的钩子。
【This function creates a hook that runs before executing a suite.】
describe('tests', async () => {
before(() => console.log('about to run some test'));
it('is a subtest', () => {
assert.ok('some relevant assertion here');
});
});
after([fn][, options])#>
fn<Function> | <AsyncFunction> 钩子函数。如果钩子使用回调,则回调函数作为第二个参数传入。默认值: 一个空操作函数。options<Object> 钩子的配置选项。支持以下属性:signal<AbortSignal> 允许中止正在进行的钩子。timeout<number> 钩子在毫秒数到达后失败。如果未指定,子测试将继承父测试的该值。默认值:Infinity。
该函数创建一个在执行套件后运行的钩子。
【This function creates a hook that runs after executing a suite.】
describe('tests', async () => {
after(() => console.log('finished running tests'));
it('is a subtest', () => {
assert.ok('some relevant assertion here');
});
});
注意: 即使测试套件中的测试失败,after 钩子也保证会运行。
beforeEach([fn][, options])#>
fn<Function> | <AsyncFunction> 钩子函数。如果钩子使用回调,则回调函数作为第二个参数传入。默认值: 一个空操作函数。options<Object> 钩子的配置选项。支持以下属性:signal<AbortSignal> 允许中止正在进行的钩子。timeout<number> 钩子在毫秒数到达后失败。如果未指定,子测试将继承父测试的该值。默认值:Infinity。
此函数创建一个在当前套件中的每个测试之前运行的钩子。
【This function creates a hook that runs before each test in the current suite.】
describe('tests', async () => {
beforeEach(() => console.log('about to run a test'));
it('is a subtest', () => {
assert.ok('some relevant assertion here');
});
});
afterEach([fn][, options])#>
fn<Function> | <AsyncFunction> 钩子函数。如果钩子使用回调,则回调函数作为第二个参数传入。默认值: 一个空操作函数。options<Object> 钩子的配置选项。支持以下属性:signal<AbortSignal> 允许中止正在进行的钩子。timeout<number> 钩子在毫秒数到达后失败。如果未指定,子测试将继承父测试的该值。默认值:Infinity。
此函数会创建一个钩子,在当前测试套件中的每个测试之后运行。即使测试失败,afterEach() 钩子也会执行。
【This function creates a hook that runs after each test in the current suite.
The afterEach() hook is run even if the test fails.】
describe('tests', async () => {
afterEach(() => console.log('finished running a test'));
it('is a subtest', () => {
assert.ok('some relevant assertion here');
});
});
assert#>
一个对象,其方法用于配置当前进程中 TestContext 对象上可用的断言。默认情况下,可以使用 node:assert 的方法和快照测试函数。
【An object whose methods are used to configure available assertions on the
TestContext objects in the current process. The methods from node:assert
and snapshot testing functions are available by default.】
可以通过将通用配置代码放在一个模块中,并使用 --require 或 --import 预加载,从而将相同的配置应用到所有文件。
【It is possible to apply the same configuration to all files by placing common
configuration code in a module
preloaded with --require or --import.】
assert.register(name, fn)#>
使用提供的名称和函数定义一个新的断言函数。如果已经存在同名的断言,它将被覆盖。
【Defines a new assertion function with the provided name and function. If an assertion already exists with the same name, it is overwritten.】
snapshot#>
一个对象,其方法用于配置当前进程中的默认快照设置。可以通过将通用配置代码放在用 --require 或 --import 预加载的模块中,将相同的配置应用于所有文件。
【An object whose methods are used to configure default snapshot settings in the
current process. It is possible to apply the same configuration to all files by
placing common configuration code in a module preloaded with --require or
--import.】
snapshot.setDefaultSnapshotSerializers(serializers)#>
serializers<Array> 一组用于快照测试的默认序列化器的同步函数数组。
此函数用于自定义测试运行程序使用的默认序列化机制。默认情况下,测试运行程序通过对提供的值调用 JSON.stringify(value, null, 2) 来执行序列化。JSON.stringify() 在循环结构和支持的数据类型方面确实存在限制。如果需要更强大的序列化机制,应使用此函数。
【This function is used to customize the default serialization mechanism used by
the test runner. By default, the test runner performs serialization by calling
JSON.stringify(value, null, 2) on the provided value. JSON.stringify() does
have limitations regarding circular structures and supported data types. If a
more robust serialization mechanism is required, this function should be used.】
snapshot.setResolveSnapshotPath(fn)#>
fn<Function> 一个用于计算快照文件位置的函数。该函数接收测试文件的路径作为其唯一参数。如果测试未关联文件(例如在 REPL 中),输入将为 undefined。fn()必须返回一个字符串,指定快照文件的位置。
此功能用于自定义用于快照测试的快照文件的位置。默认情况下,快照文件名与入口点文件名相同,并使用 .snapshot 文件扩展名。
【This function is used to customize the location of the snapshot file used for
snapshot testing. By default, the snapshot filename is the same as the entry
point filename with a .snapshot file extension.】
类:MockFunctionContext#>
【Class: MockFunctionContext】
MockFunctionContext 类用于检查或操作通过 MockTracker API 创建的模拟对象的行为。
【The MockFunctionContext class is used to inspect or manipulate the behavior of
mocks created via the MockTracker APIs.】
ctx.calls#>
- 类型: <Array>
一个 getter,用于返回用于跟踪对模拟调用的内部数组的副本。数组中的每个条目都是一个具有以下属性的对象。
【A getter that returns a copy of the internal array used to track calls to the mock. Each entry in the array is an object with the following properties.】
arguments<Array> 传递给模拟函数的参数数组。error<any> 如果模拟函数抛出错误,则此属性包含被抛出的值。默认值:undefined。result<any> 模拟函数返回的值。stack<Error> 一个Error对象,其堆栈可用于确定模拟函数调用的调用位置。target<Function> | <undefined> 如果模拟函数是一个构造函数,则此字段包含正在构造的类。否则,这将是undefined。this<any> 模拟函数的this值。
ctx.callCount()#>
- 返回值: <integer> 此模拟被调用的次数。
此函数返回此模拟被调用的次数。此函数比检查 ctx.calls.length 更高效,因为 ctx.calls 是一个 getter,它会创建内部调用跟踪数组的副本。
【This function returns the number of times that this mock has been invoked. This
function is more efficient than checking ctx.calls.length because ctx.calls
is a getter that creates a copy of the internal call tracking array.】
ctx.mockImplementation(implementation)#>
implementation<Function> | <AsyncFunction> 作为模拟的新实现要使用的函数。
此函数用于更改现有模拟的行为。
【This function is used to change the behavior of an existing mock.】
下面的示例使用 t.mock.fn() 创建了一个模拟函数,调用该模拟函数,然后将模拟实现更改为另一个函数。
【The following example creates a mock function using t.mock.fn(), calls the
mock function, and then changes the mock implementation to a different function.】
test('changes a mock behavior', (t) => {
let cnt = 0;
function addOne() {
cnt++;
return cnt;
}
function addTwo() {
cnt += 2;
return cnt;
}
const fn = t.mock.fn(addOne);
assert.strictEqual(fn(), 1);
fn.mock.mockImplementation(addTwo);
assert.strictEqual(fn(), 3);
assert.strictEqual(fn(), 5);
});
ctx.mockImplementationOnce(implementation[, onCall])#>
implementation<Function> | <AsyncFunction> 用作模拟的实现的函数,用于由onCall指定的调用次数。onCall<integer> 将使用implementation的调用次数。如果指定的调用已经发生,则会抛出异常。 默认值: 下一次调用的次数。
此函数用于更改现有模拟的单次调用行为。一旦发生调用 onCall,模拟将恢复到如果未调用 mockImplementationOnce() 时的行为。
【This function is used to change the behavior of an existing mock for a single
invocation. Once invocation onCall has occurred, the mock will revert to
whatever behavior it would have used had mockImplementationOnce() not been
called.】
下面的示例使用 t.mock.fn() 创建了一个模拟函数,调用了该模拟函数,将模拟实现更改为下一个调用使用的不同函数,然后恢复其之前的行为。
【The following example creates a mock function using t.mock.fn(), calls the
mock function, changes the mock implementation to a different function for the
next invocation, and then resumes its previous behavior.】
test('changes a mock behavior once', (t) => {
let cnt = 0;
function addOne() {
cnt++;
return cnt;
}
function addTwo() {
cnt += 2;
return cnt;
}
const fn = t.mock.fn(addOne);
assert.strictEqual(fn(), 1);
fn.mock.mockImplementationOnce(addTwo);
assert.strictEqual(fn(), 3);
assert.strictEqual(fn(), 4);
});
ctx.resetCalls()#>
重置模拟函数的调用历史。
【Resets the call history of the mock function.】
ctx.restore()#>
将模拟函数的实现重置为其原始行为。调用此函数后仍然可以使用该模拟函数。
【Resets the implementation of the mock function to its original behavior. The mock can still be used after calling this function.】
类:MockModuleContext#>
【Class: MockModuleContext】
MockModuleContext 类用于操作通过 MockTracker API 创建的模块模拟的行为。
【The MockModuleContext class is used to manipulate the behavior of module mocks
created via the MockTracker APIs.】
ctx.restore()#>
重置模拟模块的实现。
【Resets the implementation of the mock module.】
类:MockPropertyContext#>
【Class: MockPropertyContext】
MockPropertyContext 类用于检查或操作通过 MockTracker API 创建的属性模拟的行为。
【The MockPropertyContext class is used to inspect or manipulate the behavior
of property mocks created via the MockTracker APIs.】
ctx.accesses#>
- 类型: <Array>
一个 getter,用于返回用于追踪对模拟属性的访问(获取/设置)的内部数组的副本。数组中的每个条目都是一个包含以下属性的对象:
【A getter that returns a copy of the internal array used to track accesses (get/set) to the mocked property. Each entry in the array is an object with the following properties:】
type<string> 可以是'get'或'set',表示访问类型。value<any> 读取的值(对于'get')或写入的值(对于'set')。stack<Error> 一个Error对象,其堆栈可用于确定模拟函数调用的调用位置。
ctx.accessCount()#>
- 返回值:<integer> 属性被访问(读取或写入)的次数。
此函数返回该属性被访问的次数。该函数比检查 ctx.accesses.length 更高效,因为 ctx.accesses 是一个 getter,会创建内部访问跟踪数组的副本。
【This function returns the number of times that the property was accessed.
This function is more efficient than checking ctx.accesses.length because
ctx.accesses is a getter that creates a copy of the internal access tracking array.】
ctx.mockImplementation(value)#>
value<any> 要设置为模拟属性值的新值。
此函数用于更改模拟属性 getter 返回的值。
【This function is used to change the value returned by the mocked property getter.】
ctx.mockImplementationOnce(value[, onAccess])#>
value<any> 用作模拟的实现的值,用于由onAccess指定的调用次数。onAccess<integer> 将使用value的调用次数。如果指定的调用已经发生,则会抛出异常。 默认值: 下一次调用的编号。
此函数用于在单次调用中更改现有模拟的行为。一旦调用 onAccess 发生,模拟将恢复到如果未调用 mockImplementationOnce() 时的行为。
【This function is used to change the behavior of an existing mock for a single
invocation. Once invocation onAccess has occurred, the mock will revert to
whatever behavior it would have used had mockImplementationOnce() not been
called.】
下面的示例使用 t.mock.property() 创建了一个模拟函数,调用了该模拟属性,然后将模拟实现更改为下次调用时的不同值,之后再恢复其之前的行为。
【The following example creates a mock function using t.mock.property(), calls the
mock property, changes the mock implementation to a different value for the
next invocation, and then resumes its previous behavior.】
test('changes a mock behavior once', (t) => {
const obj = { foo: 1 };
const prop = t.mock.property(obj, 'foo', 5);
assert.strictEqual(obj.foo, 5);
prop.mock.mockImplementationOnce(25);
assert.strictEqual(obj.foo, 25);
assert.strictEqual(obj.foo, 5);
});
警告#>
【Caveat】
为了与其他模拟 API 保持一致,该函数将属性的获取和设置都视为访问。如果属性设置发生在相同的访问索引上,“一次性”值将被设置操作消耗,并且被模拟的属性值将更改为该“一次性”值。如果你打算“一次性”值仅用于获取操作,这可能会导致意外行为。
【For consistency with the rest of the mocking API, this function treats both property gets and sets as accesses. If a property set occurs at the same access index, the "once" value will be consumed by the set operation, and the mocked property value will be changed to the "once" value. This may lead to unexpected behavior if you intend the "once" value to only be used for a get operation.】
ctx.resetAccesses()#>
重置模拟属性的访问历史记录。
【Resets the access history of the mocked property.】
ctx.restore()#>
将模拟属性的实现重置为其原始行为。调用此函数后仍然可以使用该模拟。
【Resets the implementation of the mock property to its original behavior. The mock can still be used after calling this function.】
类:MockTracker#>
【Class: MockTracker】
MockTracker 类用于管理模拟功能。测试运行器模块提供了一个顶层的 mock 导出,它是一个 MockTracker 实例。每个测试也通过测试上下文的 mock 属性提供其自身的 MockTracker 实例。
【The MockTracker class is used to manage mocking functionality. The test runner
module provides a top level mock export which is a MockTracker instance.
Each test also provides its own MockTracker instance via the test context's
mock property.】
mock.fn([original[, implementation]][, options])#>
original<Function> | <AsyncFunction> 一个可选的函数,用于创建模拟。
默认值: 一个空操作函数。implementation<Function> | <AsyncFunction> 一个可选函数,用作original的模拟实现。这对于创建在指定次数调用中表现出某种行为然后恢复original行为的模拟非常有用。
默认值:original指定的函数。options<Object> 模拟函数的可选配置选项。支持以下属性:times<integer> 模拟使用implementation行为的次数。模拟函数被调用times次后,会自动恢复original的行为。此值必须是大于零的整数。
默认值:Infinity。
- 返回: <Proxy> 模拟函数。模拟函数包含一个特殊的
mock属性,它是MockFunctionContext的一个实例,可用于检查和更改模拟函数的行为。
此函数用于创建模拟函数。
【This function is used to create a mock function.】
以下示例创建了一个模拟函数,每次调用时使计数器增加一。times 选项用于修改模拟行为,使前两次调用将计数器增加二而不是一。
【The following example creates a mock function that increments a counter by one
on each invocation. The times option is used to modify the mock behavior such
that the first two invocations add two to the counter instead of one.】
test('mocks a counting function', (t) => {
let cnt = 0;
function addOne() {
cnt++;
return cnt;
}
function addTwo() {
cnt += 2;
return cnt;
}
const fn = t.mock.fn(addOne, addTwo, { times: 2 });
assert.strictEqual(fn(), 2);
assert.strictEqual(fn(), 4);
assert.strictEqual(fn(), 5);
assert.strictEqual(fn(), 6);
});
mock.getter(object, methodName[, implementation][, options])#>
这个函数是 MockTracker.method 的语法糖,其中 options.getter 设置为 true。
【This function is syntax sugar for MockTracker.method with options.getter
set to true.】
mock.method(object, methodName[, implementation][, options])#>
object<Object> 正在被模拟其方法的对象。methodName<string> | <symbol> 要模拟的object上方法的标识符。如果object[methodName]不是一个函数,将会抛出错误。implementation<Function> | <AsyncFunction> 可选函数,用作object[methodName]的模拟实现。默认值: 指定的object[methodName]原始方法。options<Object> 模拟方法的可选配置选项。支持以下属性:- 返回值:<Proxy> 被模拟的方法。被模拟的方法包含一个特殊的
mock属性,该属性是MockFunctionContext的一个实例,可用于检查和修改被模拟方法的行为。
此函数用于在现有对象方法上创建模拟。以下示例演示了如何在现有对象方法上创建模拟。
【This function is used to create a mock on an existing object method. The following example demonstrates how a mock is created on an existing object method.】
test('spies on an object method', (t) => {
const number = {
value: 5,
subtract(a) {
return this.value - a;
},
};
t.mock.method(number, 'subtract');
assert.strictEqual(number.subtract.mock.callCount(), 0);
assert.strictEqual(number.subtract(3), 2);
assert.strictEqual(number.subtract.mock.callCount(), 1);
const call = number.subtract.mock.calls[0];
assert.deepStrictEqual(call.arguments, [3]);
assert.strictEqual(call.result, 2);
assert.strictEqual(call.error, undefined);
assert.strictEqual(call.target, undefined);
assert.strictEqual(call.this, number);
});
mock.module(specifier[, options])#>
specifier<string> | <URL> 一个标识要模拟的模块的字符串。options<Object> 模拟模块的可选配置选项。支持以下属性:cache<boolean> 如果为false,每次调用require()或import()都会生成一个新的模拟模块。如果为true,后续调用将返回相同的模块模拟,并且该模拟模块会被插入到 CommonJS 缓存中。 默认值: false.defaultExport<any> 用作模拟模块默认导出的可选值。如果未提供此值,ESM 模拟将不包含默认导出。如果模拟的是 CommonJS 或内置模块,则此设置用作module.exports的值。如果未提供此值,CJS 和内置模拟将使用空对象作为module.exports的值。namedExports<Object> 一个可选对象,其键和值用于创建模拟模块的命名导出。如果模拟的是 CommonJS 模块或内置模块,这些值会被复制到module.exports上。因此,如果一个模拟同时具有命名导出和非对象默认导出,当它作为 CJS 或内置模块使用时,模拟将会抛出异常。
- 返回:<MockModuleContext> 可用于操作模拟对象的对象。
此函数用于模拟 ECMAScript 模块、CommonJS 模块、JSON 模块和 Node.js 内置模块的导出。在模拟之前对原始模块的任何引用都不受影响。要启用模块模拟,必须使用 --experimental-test-module-mocks 命令行标志启动 Node.js。
【This function is used to mock the exports of ECMAScript modules, CommonJS modules, JSON modules, and
Node.js builtin modules. Any references to the original module prior to mocking are not impacted. In
order to enable module mocking, Node.js must be started with the
--experimental-test-module-mocks command-line flag.】
以下示例演示了如何为模块创建模拟。
【The following example demonstrates how a mock is created for a module.】
test('mocks a builtin module in both module systems', async (t) => {
// Create a mock of 'node:readline' with a named export named 'fn', which
// does not exist in the original 'node:readline' module.
const mock = t.mock.module('node:readline', {
namedExports: { fn() { return 42; } },
});
let esmImpl = await import('node:readline');
let cjsImpl = require('node:readline');
// cursorTo() is an export of the original 'node:readline' module.
assert.strictEqual(esmImpl.cursorTo, undefined);
assert.strictEqual(cjsImpl.cursorTo, undefined);
assert.strictEqual(esmImpl.fn(), 42);
assert.strictEqual(cjsImpl.fn(), 42);
mock.restore();
// The mock is restored, so the original builtin module is returned.
esmImpl = await import('node:readline');
cjsImpl = require('node:readline');
assert.strictEqual(typeof esmImpl.cursorTo, 'function');
assert.strictEqual(typeof cjsImpl.cursorTo, 'function');
assert.strictEqual(esmImpl.fn, undefined);
assert.strictEqual(cjsImpl.fn, undefined);
});
mock.property(object, propertyName[, value])#>
object<Object> 要被模拟其值的对象。propertyName<string> | <symbol>object上要模拟的属性标识符。value<any> 用作object[propertyName]的可选模拟值。默认值: 原始属性值。- 返回值: <Proxy> 被模拟对象的代理。被模拟的对象包含一个特殊的
mock属性,它是MockPropertyContext的实例,可用于检查和更改被模拟属性的行为。
为对象上的属性值创建一个模拟。这允许你跟踪和控制对特定属性的访问,包括读取(getter)或写入(setter)的次数,并在模拟后恢复原始值。
【Creates a mock for a property value on an object. This allows you to track and control access to a specific property, including how many times it is read (getter) or written (setter), and to restore the original value after mocking.】
test('mocks a property value', (t) => {
const obj = { foo: 42 };
const prop = t.mock.property(obj, 'foo', 100);
assert.strictEqual(obj.foo, 100);
assert.strictEqual(prop.mock.accessCount(), 1);
assert.strictEqual(prop.mock.accesses[0].type, 'get');
assert.strictEqual(prop.mock.accesses[0].value, 100);
obj.foo = 200;
assert.strictEqual(prop.mock.accessCount(), 2);
assert.strictEqual(prop.mock.accesses[1].type, 'set');
assert.strictEqual(prop.mock.accesses[1].value, 200);
prop.mock.restore();
assert.strictEqual(obj.foo, 42);
});
mock.reset()#>
此函数会恢复由该 MockTracker 之前创建的所有模拟的默认行为,并将这些模拟与 MockTracker 实例解绑。解绑后,模拟仍然可以使用,但 MockTracker 实例将无法再重置它们的行为或以其他方式与它们互动。
【This function restores the default behavior of all mocks that were previously
created by this MockTracker and disassociates the mocks from the
MockTracker instance. Once disassociated, the mocks can still be used, but the
MockTracker instance can no longer be used to reset their behavior or
otherwise interact with them.】
每个测试完成后,将在测试上下文的 MockTracker 上调用此函数。如果全局 MockTracker 使用频繁,建议手动调用此函数。
【After each test completes, this function is called on the test context's
MockTracker. If the global MockTracker is used extensively, calling this
function manually is recommended.】
mock.restoreAll()#>
此函数会恢复由该 MockTracker 之前创建的所有模拟的默认行为。与 mock.reset() 不同,mock.restoreAll() 不会将模拟与 MockTracker 实例解除关联。
【This function restores the default behavior of all mocks that were previously
created by this MockTracker. Unlike mock.reset(), mock.restoreAll() does
not disassociate the mocks from the MockTracker instance.】
mock.setter(object, methodName[, implementation][, options])#>
这个函数是 MockTracker.method 的语法糖,其 options.setter 设置为 true。
【This function is syntax sugar for MockTracker.method with options.setter
set to true.】
类:MockTimers#>
【Class: MockTimers】
模拟定时器是一种常用于软件测试的技术,用于模拟和控制定时器的行为,例如 setInterval 和 setTimeout,而无需实际等待指定的时间间隔。
【Mocking timers is a technique commonly used in software testing to simulate and
control the behavior of timers, such as setInterval and setTimeout,
without actually waiting for the specified time intervals.】
MockTimers 也能够模拟 Date 对象。
【MockTimers is also able to mock the Date object.】
MockTracker 提供了一个顶层的 timers 导出,它是一个 MockTimers 实例。
【The MockTracker provides a top-level timers export
which is a MockTimers instance.】
timers.enable([enableOptions])#>
启用指定定时器的定时器模拟。
【Enables timer mocking for the specified timers.】
enableOptions<Object> 启用定时器模拟的可选配置选项。支持以下属性:apis<Array> 一个可选的数组,包含要模拟的定时器。目前支持的定时器值有'setInterval'、'setTimeout'、'setImmediate'和'Date'。默认值:['setInterval', 'setTimeout', 'setImmediate', 'Date']。如果未提供数组,默认将模拟所有与时间相关的 API('setInterval'、'clearInterval'、'setTimeout'、'clearTimeout'、'setImmediate'、'clearImmediate'和'Date')。now<number> | <Date> 一个可选的数字或 Date 对象,表示用作Date.now()的初始时间(毫秒)。默认值:0。
注意: 当你为特定计时器启用模拟时,其关联的清除函数也会被隐式地模拟。
注意: 模拟 Date 会影响被模拟计时器的行为,因为它们使用相同的内部时钟。
不设置初始时间的示例用法:
【Example usage without setting initial time:】
import { mock } from 'node:test';
mock.timers.enable({ apis: ['setInterval'] });const { mock } = require('node:test');
mock.timers.enable({ apis: ['setInterval'] });
上面的示例启用了对 setInterval 定时器的模拟,并隐式模拟了 clearInterval 函数。只有来自 node:timers、node:timers/promises 和 globalThis 的 setInterval 和 clearInterval 函数会被模拟。
【The above example enables mocking for the setInterval timer and
implicitly mocks the clearInterval function. Only the setInterval
and clearInterval functions from node:timers,
node:timers/promises, and
globalThis will be mocked.】
设置初始时间的示例用法
【Example usage with initial time set】
import { mock } from 'node:test';
mock.timers.enable({ apis: ['Date'], now: 1000 });const { mock } = require('node:test');
mock.timers.enable({ apis: ['Date'], now: 1000 });
将初始 Date 对象设置为时间的示例用法
【Example usage with initial Date object as time set】
import { mock } from 'node:test';
mock.timers.enable({ apis: ['Date'], now: new Date() });const { mock } = require('node:test');
mock.timers.enable({ apis: ['Date'], now: new Date() });
或者,如果你在没有任何参数的情况下调用 mock.timers.enable():
【Alternatively, if you call mock.timers.enable() without any parameters:】
所有计时器('setInterval'、'clearInterval'、'setTimeout'、'clearTimeout'、'setImmediate' 和 'clearImmediate')都将被模拟。从 node:timers、node:timers/promises 和 globalThis 导入的 setInterval、clearInterval、setTimeout、clearTimeout、setImmediate 和 clearImmediate 函数也将被模拟。全局 Date 对象也会被模拟。
【All timers ('setInterval', 'clearInterval', 'setTimeout', 'clearTimeout',
'setImmediate', and 'clearImmediate') will be mocked. The setInterval,
clearInterval, setTimeout, clearTimeout, setImmediate, and
clearImmediate functions from node:timers, node:timers/promises, and
globalThis will be mocked. As well as the global Date object.】
timers.reset()#>
此函数会恢复由该 MockTimers 实例先前创建的所有模拟的默认行为,并将这些模拟与 MockTracker 实例取消关联。
【This function restores the default behavior of all mocks that were previously
created by this MockTimers instance and disassociates the mocks
from the MockTracker instance.】
注意: 每次测试完成后,该函数会在测试上下文的 MockTracker 上被调用。
import { mock } from 'node:test';
mock.timers.reset();const { mock } = require('node:test');
mock.timers.reset();
timers[Symbol.dispose]()#>
调用 timers.reset()。
【Calls timers.reset().】
timers.tick([milliseconds])#>
提前所有模拟定时器的时间。
【Advances time for all mocked timers.】
milliseconds<number> 以毫秒为单位推进计时器的时间量。默认值:1。
注意: 这与 Node.js 中 setTimeout 的行为有所不同,并且只接受正数。在 Node.js 中,setTimeout 支持负数仅出于网页兼容性的原因。
以下示例模拟了一个 setTimeout 函数,并通过使用 .tick 来推进时间,从而触发所有待处理的定时器。
【The following example mocks a setTimeout function and
by using .tick advances in
time triggering all pending timers.】
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({ apis: ['setTimeout'] });
setTimeout(fn, 9999);
assert.strictEqual(fn.mock.callCount(), 0);
// Advance in time
context.mock.timers.tick(9999);
assert.strictEqual(fn.mock.callCount(), 1);
});const assert = require('node:assert');
const { test } = require('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({ apis: ['setTimeout'] });
setTimeout(fn, 9999);
assert.strictEqual(fn.mock.callCount(), 0);
// Advance in time
context.mock.timers.tick(9999);
assert.strictEqual(fn.mock.callCount(), 1);
});
或者,.tick 函数可以被多次调用
【Alternatively, the .tick function can be called many times】
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({ apis: ['setTimeout'] });
const nineSecs = 9000;
setTimeout(fn, nineSecs);
const threeSeconds = 3000;
context.mock.timers.tick(threeSeconds);
context.mock.timers.tick(threeSeconds);
context.mock.timers.tick(threeSeconds);
assert.strictEqual(fn.mock.callCount(), 1);
});const assert = require('node:assert');
const { test } = require('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({ apis: ['setTimeout'] });
const nineSecs = 9000;
setTimeout(fn, nineSecs);
const threeSeconds = 3000;
context.mock.timers.tick(threeSeconds);
context.mock.timers.tick(threeSeconds);
context.mock.timers.tick(threeSeconds);
assert.strictEqual(fn.mock.callCount(), 1);
});
使用 .tick 推进时间也会推进任何在模拟启用后创建的 Date 对象的时间(如果 Date 也被设置为模拟)。
【Advancing time using .tick will also advance the time for any Date object
created after the mock was enabled (if Date was also set to be mocked).】
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({ apis: ['setTimeout', 'Date'] });
setTimeout(fn, 9999);
assert.strictEqual(fn.mock.callCount(), 0);
assert.strictEqual(Date.now(), 0);
// Advance in time
context.mock.timers.tick(9999);
assert.strictEqual(fn.mock.callCount(), 1);
assert.strictEqual(Date.now(), 9999);
});const assert = require('node:assert');
const { test } = require('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({ apis: ['setTimeout', 'Date'] });
setTimeout(fn, 9999);
assert.strictEqual(fn.mock.callCount(), 0);
assert.strictEqual(Date.now(), 0);
// Advance in time
context.mock.timers.tick(9999);
assert.strictEqual(fn.mock.callCount(), 1);
assert.strictEqual(Date.now(), 9999);
});
使用明确的函数#>
【Using clear functions】
如前所述,所有来自定时器的清除函数(clearTimeout、clearInterval 和 clearImmediate)都会被隐式模拟。来看这个使用 setTimeout 的示例:
【As mentioned, all clear functions from timers (clearTimeout, clearInterval,and
clearImmediate) are implicitly mocked. Take a look at this example using setTimeout:】
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();
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['setTimeout'] });
const id = setTimeout(fn, 9999);
// Implicitly mocked as well
clearTimeout(id);
context.mock.timers.tick(9999);
// As that setTimeout was cleared the mock function will never be called
assert.strictEqual(fn.mock.callCount(), 0);
});const assert = require('node:assert');
const { test } = require('node:test');
test('mocks setTimeout to be executed synchronously without having to actually wait for it', (context) => {
const fn = context.mock.fn();
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['setTimeout'] });
const id = setTimeout(fn, 9999);
// Implicitly mocked as well
clearTimeout(id);
context.mock.timers.tick(9999);
// As that setTimeout was cleared the mock function will never be called
assert.strictEqual(fn.mock.callCount(), 0);
});
使用 Node.js 定时器模块#>
【Working with Node.js timers modules】
一旦你启用模拟计时器,node:timers、node:timers/promises 模块,以及来自 Node.js 全局上下文的计时器就会被启用:
【Once you enable mocking timers, node:timers, node:timers/promises modules, and timers from the Node.js global context are enabled:】
注意: 像 import { setTimeout } from 'node:timers' 这样的解构函数目前不被此 API 支持。
import assert from 'node:assert';
import { test } from 'node:test';
import nodeTimers from 'node:timers';
import nodeTimersPromises from 'node:timers/promises';
test('mocks setTimeout to be executed synchronously without having to actually wait for it', async (context) => {
const globalTimeoutObjectSpy = context.mock.fn();
const nodeTimerSpy = context.mock.fn();
const nodeTimerPromiseSpy = context.mock.fn();
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['setTimeout'] });
setTimeout(globalTimeoutObjectSpy, 9999);
nodeTimers.setTimeout(nodeTimerSpy, 9999);
const promise = nodeTimersPromises.setTimeout(9999).then(nodeTimerPromiseSpy);
// Advance in time
context.mock.timers.tick(9999);
assert.strictEqual(globalTimeoutObjectSpy.mock.callCount(), 1);
assert.strictEqual(nodeTimerSpy.mock.callCount(), 1);
await promise;
assert.strictEqual(nodeTimerPromiseSpy.mock.callCount(), 1);
});const assert = require('node:assert');
const { test } = require('node:test');
const nodeTimers = require('node:timers');
const nodeTimersPromises = require('node:timers/promises');
test('mocks setTimeout to be executed synchronously without having to actually wait for it', async (context) => {
const globalTimeoutObjectSpy = context.mock.fn();
const nodeTimerSpy = context.mock.fn();
const nodeTimerPromiseSpy = context.mock.fn();
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['setTimeout'] });
setTimeout(globalTimeoutObjectSpy, 9999);
nodeTimers.setTimeout(nodeTimerSpy, 9999);
const promise = nodeTimersPromises.setTimeout(9999).then(nodeTimerPromiseSpy);
// Advance in time
context.mock.timers.tick(9999);
assert.strictEqual(globalTimeoutObjectSpy.mock.callCount(), 1);
assert.strictEqual(nodeTimerSpy.mock.callCount(), 1);
await promise;
assert.strictEqual(nodeTimerPromiseSpy.mock.callCount(), 1);
});
在 Node.js 中,node:timers/promises 的 setInterval 是一个 AsyncGenerator,并且也被此 API 支持:
【In Node.js, setInterval from node:timers/promises
is an AsyncGenerator and is also supported by this API:】
import assert from 'node:assert';
import { test } from 'node:test';
import nodeTimersPromises from 'node:timers/promises';
test('should tick five times testing a real use case', async (context) => {
context.mock.timers.enable({ apis: ['setInterval'] });
const expectedIterations = 3;
const interval = 1000;
const startedAt = Date.now();
async function run() {
const times = [];
for await (const time of nodeTimersPromises.setInterval(interval, startedAt)) {
times.push(time);
if (times.length === expectedIterations) break;
}
return times;
}
const r = run();
context.mock.timers.tick(interval);
context.mock.timers.tick(interval);
context.mock.timers.tick(interval);
const timeResults = await r;
assert.strictEqual(timeResults.length, expectedIterations);
for (let it = 1; it < expectedIterations; it++) {
assert.strictEqual(timeResults[it - 1], startedAt + (interval * it));
}
});const assert = require('node:assert');
const { test } = require('node:test');
const nodeTimersPromises = require('node:timers/promises');
test('should tick five times testing a real use case', async (context) => {
context.mock.timers.enable({ apis: ['setInterval'] });
const expectedIterations = 3;
const interval = 1000;
const startedAt = Date.now();
async function run() {
const times = [];
for await (const time of nodeTimersPromises.setInterval(interval, startedAt)) {
times.push(time);
if (times.length === expectedIterations) break;
}
return times;
}
const r = run();
context.mock.timers.tick(interval);
context.mock.timers.tick(interval);
context.mock.timers.tick(interval);
const timeResults = await r;
assert.strictEqual(timeResults.length, expectedIterations);
for (let it = 1; it < expectedIterations; it++) {
assert.strictEqual(timeResults[it - 1], startedAt + (interval * it));
}
});
timers.runAll()#>
立即触发所有挂起的模拟计时器。如果 Date 对象也被模拟,它也会将 Date 对象推进到最远计时器的时间。
【Triggers all pending mocked timers immediately. If the Date object is also
mocked, it will also advance the Date object to the furthest timer's time.】
下面的示例会立即触发所有待处理的计时器,使它们无需任何延迟就执行。
【The example below triggers all pending timers immediately, causing them to execute without any delay.】
import assert from 'node:assert';
import { test } from 'node:test';
test('runAll functions following the given order', (context) => {
context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
const results = [];
setTimeout(() => results.push(1), 9999);
// Notice that if both timers have the same timeout,
// the order of execution is guaranteed
setTimeout(() => results.push(3), 8888);
setTimeout(() => results.push(2), 8888);
assert.deepStrictEqual(results, []);
context.mock.timers.runAll();
assert.deepStrictEqual(results, [3, 2, 1]);
// The Date object is also advanced to the furthest timer's time
assert.strictEqual(Date.now(), 9999);
});const assert = require('node:assert');
const { test } = require('node:test');
test('runAll functions following the given order', (context) => {
context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
const results = [];
setTimeout(() => results.push(1), 9999);
// Notice that if both timers have the same timeout,
// the order of execution is guaranteed
setTimeout(() => results.push(3), 8888);
setTimeout(() => results.push(2), 8888);
assert.deepStrictEqual(results, []);
context.mock.timers.runAll();
assert.deepStrictEqual(results, [3, 2, 1]);
// The Date object is also advanced to the furthest timer's time
assert.strictEqual(Date.now(), 9999);
});
注意: runAll() 函数专门用于在定时器模拟的环境中触发定时器。它不会对真实系统时钟或模拟环境之外的实际定时器产生任何影响。
timers.setTime(milliseconds)#>
设置当前的 Unix 时间戳,该时间戳将被用作任何模拟 Date 对象的参考。
【Sets the current Unix timestamp that will be used as reference for any mocked
Date objects.】
import assert from 'node:assert';
import { test } from 'node:test';
test('runAll functions following the given order', (context) => {
const now = Date.now();
const setTime = 1000;
// Date.now is not mocked
assert.deepStrictEqual(Date.now(), now);
context.mock.timers.enable({ apis: ['Date'] });
context.mock.timers.setTime(setTime);
// Date.now is now 1000
assert.strictEqual(Date.now(), setTime);
});const assert = require('node:assert');
const { test } = require('node:test');
test('setTime replaces current time', (context) => {
const now = Date.now();
const setTime = 1000;
// Date.now is not mocked
assert.deepStrictEqual(Date.now(), now);
context.mock.timers.enable({ apis: ['Date'] });
context.mock.timers.setTime(setTime);
// Date.now is now 1000
assert.strictEqual(Date.now(), setTime);
});
日期和定时器一起工作#>
【Dates and Timers working together】
日期和定时器对象相互依赖。如果你使用 setTime() 将当前时间传递给模拟的 Date 对象,使用 setTimeout 和 setInterval 设置的定时器将不会受到影响。
【Dates and timer objects are dependent on each other. If you use setTime() to
pass the current time to the mocked Date object, the set timers with
setTimeout and setInterval will not be affected.】
然而,tick 方法会推进模拟的 Date 对象。
【However, the tick method will advanced the mocked Date object.】
import assert from 'node:assert';
import { test } from 'node:test';
test('runAll functions following the given order', (context) => {
context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
const results = [];
setTimeout(() => results.push(1), 9999);
assert.deepStrictEqual(results, []);
context.mock.timers.setTime(12000);
assert.deepStrictEqual(results, []);
// The date is advanced but the timers don't tick
assert.strictEqual(Date.now(), 12000);
});const assert = require('node:assert');
const { test } = require('node:test');
test('runAll functions following the given order', (context) => {
context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
const results = [];
setTimeout(() => results.push(1), 9999);
assert.deepStrictEqual(results, []);
context.mock.timers.setTime(12000);
assert.deepStrictEqual(results, []);
// The date is advanced but the timers don't tick
assert.strictEqual(Date.now(), 12000);
});
类:TestsStream#>
【Class: TestsStream】
- 继承 <Readable>
成功调用 run() 方法将返回一个新的 <TestsStream> 对象,流式传输一系列表示测试执行的事件。TestsStream 将按照测试定义的顺序触发事件
【A successful call to run() method will return a new <TestsStream>
object, streaming a series of events representing the execution of the tests.
TestsStream will emit events, in the order of the tests definition】
有些事件保证按照测试定义的顺序触发,而有些事件则按照测试执行的顺序触发。
【Some of the events are guaranteed to be emitted in the same order as the tests are defined, while others are emitted in the order that the tests execute.】
事件:'test:coverage'#>
【Event: 'test:coverage'】
data<Object>summary<Object> 包含覆盖率报告的对象。files<Array> 各个文件的覆盖率报告数组。每个报告都是一个具有以下结构的对象:path<string> 文件的绝对路径。totalLineCount<number> 行的总数。totalBranchCount<number> 分支的总数。totalFunctionCount<number> 函数的总数。coveredLineCount<number> 行覆盖的数量。coveredBranchCount<number> 分支覆盖的数量。coveredFunctionCount<number> 函数覆盖的数量。coveredLinePercent<number> 行覆盖的比例。coveredBranchPercent<number> 分支覆盖的百分比。coveredFunctionPercent<number> 函数覆盖的百分比。functions<Array> 表示函数覆盖的一组函数。branches<Array> 表示分支覆盖率的一组分支。lines<Array> 一组表示行号及其被覆盖次数的行。
thresholds<Object> 一个包含每种保障类型是否有保障的对象。totals<Object> 包含所有文件覆盖摘要的对象。totalLineCount<number> 行的总数。totalBranchCount<number> 分支的总数。totalFunctionCount<number> 函数的总数。coveredLineCount<number> 行覆盖的数量。coveredBranchCount<number> 分支覆盖的数量。coveredFunctionCount<number> 函数覆盖的数量。coveredLinePercent<number> 行覆盖的比例。coveredBranchPercent<number> 分支覆盖的百分比。coveredFunctionPercent<number> 函数覆盖的百分比。
workingDirectory<string> 代码覆盖开始时的工作目录。这在测试更改了 Node.js 进程的工作目录时,用于显示相对路径名非常有用。
nesting<number> 测试的嵌套层级。
启用代码覆盖率并且所有测试都已完成时触发。
【Emitted when code coverage is enabled and all tests have completed.】
事件:'test:complete'#>
【Event: 'test:complete'】
data<Object>column<number> | <undefined> 测试定义所在的列号,如果测试是通过 REPL 运行的,则为undefined。details<Object> 额外的执行元数据。passed<boolean> 测试是否通过。duration_ms<number> 测试的持续时间,单位为毫秒。error<Error> | <undefined> 如果测试未通过,则封装测试抛出的错误的错误对象。cause<Error> 测试实际抛出的错误。
type<string> | <undefined> 测试的类型,用于标记这是一个套件。
file<string> | <undefined> 测试文件的路径,如果测试通过 REPL 运行则为undefined。line<number> | <undefined> 测试定义所在的行号,如果测试是通过 REPL 运行的,则为undefined。name<string> 测试名称。nesting<number> 测试的嵌套级别。testNumber<number> 测试的序号。todo<string> | <boolean> | <undefined> 如果调用context.todo,则存在。skip<string> | <boolean> | <undefined> 如果调用context.skip,则存在。
当测试完成其执行时触发此事件。该事件的触发顺序不按照测试定义的顺序。对应的按顺序声明的事件是 'test:pass' 和 'test:fail'。
【Emitted when a test completes its execution.
This event is not emitted in the same order as the tests are
defined.
The corresponding declaration ordered events are 'test:pass' and 'test:fail'.】
事件:'test:dequeue'#>
【Event: 'test:dequeue'】
data<Object>column<number> | <undefined> 测试定义所在的列号,如果测试是在 REPL 中运行则为undefined。file<string> | <undefined> 测试文件的路径,如果测试是在 REPL 中运行则为undefined。line<number> | <undefined> 测试定义所在的行号,如果测试是在 REPL 中运行则为undefined。name<string> 测试名称。nesting<number> 测试的嵌套层级。type<string> 测试类型。可以是'suite'或'test'。
当一个测试从队列中取出并准备执行时触发此事件。该事件不能保证与测试定义的顺序一致。对应的声明顺序事件是 'test:start'。
【Emitted when a test is dequeued, right before it is executed.
This event is not guaranteed to be emitted in the same order as the tests are
defined. The corresponding declaration ordered event is 'test:start'.】
事件:'test:diagnostic'#>
【Event: 'test:diagnostic'】
data<Object>column<number> | <undefined> 测试定义所在的列号,如果通过 REPL 运行测试,则为undefined。file<string> | <undefined> 测试文件的路径,如果通过 REPL 运行测试,则为undefined。line<number> | <undefined> 测试定义所在的行号,如果通过 REPL 运行测试,则为undefined。message<string> 诊断信息。nesting<number> 测试的嵌套级别。level<string> 诊断信息的严重性等级。可能取值有:'info':信息性消息。'warn':警告。'error':错误。
当调用 context.diagnostic 时触发此事件。此事件保证按照测试定义的顺序触发。
【Emitted when context.diagnostic is called.
This event is guaranteed to be emitted in the same order as the tests are
defined.】
事件:'test:enqueue'#>
【Event: 'test:enqueue'】
data<Object>column<number> | <undefined> 测试定义所在的列号,如果测试是在 REPL 中运行则为undefined。file<string> | <undefined> 测试文件的路径,如果测试是在 REPL 中运行则为undefined。line<number> | <undefined> 测试定义所在的行号,如果测试是在 REPL 中运行则为undefined。name<string> 测试名称。nesting<number> 测试的嵌套层级。type<string> 测试类型。可以是'suite'或'test'。
当测试排队执行时触发。
【Emitted when a test is enqueued for execution.】
事件:'test:fail'#>
【Event: 'test:fail'】
data<Object>column<number> | <undefined> 测试被定义的列号,如果测试是通过 REPL 运行的,则为undefined。details<Object> 额外的执行元数据。duration_ms<number> 测试的持续时间(毫秒)。error<Error> 一个封装测试中抛出错误的错误。cause<Error> 测试实际抛出的错误。
type<string> | <undefined> 测试的类型,用于表示这是否是一个测试套件。attempt<number> | <undefined> 测试运行的尝试次数,仅在使用--test-rerun-failures标志时出现。
file<string> | <undefined> 测试文件的路径,如果测试是通过 REPL 运行,则为undefined。line<number> | <undefined> 测试定义所在的行号,如果测试是通过 REPL 运行的,则为undefined。name<string> 测试名称。nesting<number> 测试的嵌套层级。testNumber<number> 测试的序号。todo<string> | <boolean> | <undefined> 如果调用了context.todo,则存在skip<string> | <boolean> | <undefined> 如果调用了context.skip,则存在
当测试失败时触发。
此事件保证按测试定义的顺序触发。
对应的按执行顺序的事件是 'test:complete'。
【Emitted when a test fails.
This event is guaranteed to be emitted in the same order as the tests are
defined.
The corresponding execution ordered event is 'test:complete'.】
事件:'test:pass'#>
【Event: 'test:pass'】
data<Object>column<number> | <undefined> 测试被定义的列号,如果测试是通过 REPL 运行的,则为undefined。details<Object> 额外的执行元数据。duration_ms<number> 测试的持续时间(毫秒)。type<string> | <undefined> 测试的类型,用于表示这是否是一个测试套件。attempt<number> | <undefined> 测试运行的尝试次数,仅在使用--test-rerun-failures标志时出现。- “passed_on_attempt” <number> | <undefined> 测试通过的尝试编号,
仅在使用
--test-rerun-failures标志时才存在。
file<string> | <undefined> 测试文件的路径,如果测试是通过 REPL 运行,则为undefined。line<number> | <undefined> 测试定义所在的行号,如果测试是通过 REPL 运行的,则为undefined。name<string> 测试名称。nesting<number> 测试的嵌套层级。testNumber<number> 测试的序号。todo<string> | <boolean> | <undefined> 如果调用了context.todo,则存在skip<string> | <boolean> | <undefined> 如果调用了context.skip,则存在
当测试通过时触发。
该事件保证按照测试定义的顺序触发。
对应的按执行顺序的事件是 'test:complete'。
【Emitted when a test passes.
This event is guaranteed to be emitted in the same order as the tests are
defined.
The corresponding execution ordered event is 'test:complete'.】
事件:'test:plan'#>
【Event: 'test:plan'】
data<Object>column<number> | <undefined> 测试定义的列号,如果测试是在 REPL 中运行,则为undefined。file<string> | <undefined> 测试文件的路径,如果测试是在 REPL 中运行,则为undefined。line<number> | <undefined> 测试定义的行号,如果测试是在 REPL 中运行,则为undefined。nesting<number> 测试的嵌套层级。count<number> 已运行的子测试数量。
当给定测试的所有子测试完成时触发。该事件保证按照测试定义的顺序触发。
【Emitted when all subtests have completed for a given test. This event is guaranteed to be emitted in the same order as the tests are defined.】
事件:'test:start'#>
【Event: 'test:start'】
data<Object>column<number> | <undefined> 测试定义的列号,如果是通过 REPL 运行的测试,则为undefined。file<string> | <undefined> 测试文件的路径,如果是通过 REPL 运行的测试,则为undefined。line<number> | <undefined> 测试定义的行号,如果是通过 REPL 运行的测试,则为undefined。name<string> 测试名称。nesting<number> 测试的嵌套级别。
当测试开始报告其自身及其子测试的状态时触发此事件。该事件保证按照测试定义的顺序触发。对应的按执行顺序的事件是 'test:dequeue'。
【Emitted when a test starts reporting its own and its subtests status.
This event is guaranteed to be emitted in the same order as the tests are
defined.
The corresponding execution ordered event is 'test:dequeue'.】
事件:'test:stderr'#>
【Event: 'test:stderr'】
当正在运行的测试写入 stderr 时触发。只有在传递了 --test 标志时才会触发此事件。此事件不保证按测试定义的顺序触发。
【Emitted when a running test writes to stderr.
This event is only emitted if --test flag is passed.
This event is not guaranteed to be emitted in the same order as the tests are
defined.】
事件:'test:stdout'#>
【Event: 'test:stdout'】
当正在运行的测试写入 stdout 时触发。
只有在传递了 --test 标志时才会触发此事件。
此事件不能保证按测试定义的顺序触发。
【Emitted when a running test writes to stdout.
This event is only emitted if --test flag is passed.
This event is not guaranteed to be emitted in the same order as the tests are
defined.】
事件:'test:summary'#>
【Event: 'test:summary'】
data<Object>counts<Object> 一个包含各种测试结果计数的对象。duration_ms<number> 测试运行的持续时间(毫秒)。- 'file' <string> | <undefined> 生成 摘要。如果摘要对应多个文件,则该值为 “未定义”。
success<boolean> 指示测试运行是否被认为成功。如果发生任何错误情况,例如测试失败或未达到覆盖率阈值,该值将被设置为false。
在测试运行完成时触发。当测试运行完成时,该事件包含相关的指标,对于判断测试运行是通过还是失败非常有用。如果使用进程级测试隔离,则每个测试文件都会生成一个 'test:summary' 事件,此外还会生成最终的累计汇总。
【Emitted when a test run completes. This event contains metrics pertaining to
the completed test run, and is useful for determining if a test run passed or
failed. If process-level test isolation is used, a 'test:summary' event is
generated for each test file in addition to a final cumulative summary.】
事件:'test:watch:drained'#>
【Event: 'test:watch:drained'】
当没有更多测试排队等待以监视模式执行时触发。
【Emitted when no more tests are queued for execution in watch mode.】
事件:'test:watch:restarted'#>
【Event: 'test:watch:restarted'】
在监视模式下,由于文件更改而重新启动一个或多个测试时触发。
【Emitted when one or more tests are restarted due to a file change in watch mode.】
类:TestContext#>
【Class: TestContext】
每个测试函数都会传入一个 TestContext 实例,以便与测试运行器进行交互。然而,TestContext 构造函数并未作为 API 的一部分公开。
【An instance of TestContext is passed to each test function in order to
interact with the test runner. However, the TestContext constructor is not
exposed as part of the API.】
context.before([fn][, options])#>
fn<Function> | <AsyncFunction> 钩子函数。该函数的第一个参数是一个TestContext对象。如果钩子使用回调函数,回调函数将作为第二个参数传入。默认值: 一个空操作函数。options<Object> 钩子的配置选项。支持以下属性:signal<AbortSignal> 允许中止正在进行的钩子。timeout<number> 钩子在多少毫秒后失败的时间。如果未指定,子测试会继承其父测试的该值。默认值:Infinity。
此函数用于在当前测试的子测试运行前创建一个钩子。
【This function is used to create a hook running before subtest of the current test.】
context.beforeEach([fn][, options])#>
fn<Function> | <AsyncFunction> 钩子函数。该函数的第一个参数是一个TestContext对象。如果钩子使用回调函数,回调函数将作为第二个参数传入。默认值: 一个空操作函数。options<Object> 钩子的配置选项。支持以下属性:signal<AbortSignal> 允许中止正在进行的钩子。timeout<number> 钩子在多少毫秒后失败的时间。如果未指定,子测试会继承其父测试的该值。默认值:Infinity。
此函数用于在当前测试的每个子测试之前创建一个钩子。
【This function is used to create a hook running before each subtest of the current test.】
test('top level test', async (t) => {
t.beforeEach((t) => t.diagnostic(`about to run ${t.name}`));
await t.test(
'This is a subtest',
(t) => {
assert.ok('some relevant assertion here');
},
);
});
context.after([fn][, options])#>
fn<Function> | <AsyncFunction> 钩子函数。该函数的第一个参数是一个TestContext对象。如果钩子使用回调函数,回调函数将作为第二个参数传入。默认值: 一个空操作函数。options<Object> 钩子的配置选项。支持以下属性:signal<AbortSignal> 允许中止正在进行的钩子。timeout<number> 钩子在多少毫秒后失败的时间。如果未指定,子测试会继承其父测试的该值。默认值:Infinity。
此函数用于创建一个在当前测试结束后运行的钩子。
【This function is used to create a hook that runs after the current test finishes.】
test('top level test', async (t) => {
t.after((t) => t.diagnostic(`finished running ${t.name}`));
assert.ok('some relevant assertion here');
});
context.afterEach([fn][, options])#>
fn<Function> | <AsyncFunction> 钩子函数。该函数的第一个参数是一个TestContext对象。如果钩子使用回调函数,回调函数将作为第二个参数传入。默认值: 一个空操作函数。options<Object> 钩子的配置选项。支持以下属性:signal<AbortSignal> 允许中止正在进行的钩子。timeout<number> 钩子在多少毫秒后失败的时间。如果未指定,子测试会继承其父测试的该值。默认值:Infinity。
此函数用于创建一个钩子,在当前测试的每个子测试之后运行。
【This function is used to create a hook running after each subtest of the current test.】
test('top level test', async (t) => {
t.afterEach((t) => t.diagnostic(`finished running ${t.name}`));
await t.test(
'This is a subtest',
(t) => {
assert.ok('some relevant assertion here');
},
);
});
context.assert#>
一个包含绑定到 context 的断言方法的对象。这里暴露了 node:assert 模块的顶层函数,以便创建测试计划。
【An object containing assertion methods bound to context. The top-level
functions from the node:assert module are exposed here for the purpose of
creating test plans.】
test('test', (t) => {
t.plan(1);
t.assert.strictEqual(true, true);
});
context.assert.fileSnapshot(value, path[, options])#>
value<any> 要序列化为字符串的值。如果 Node.js 是使用--test-update-snapshots标志启动的,则序列化的值会写入path。否则,序列化的值会与现有快照文件的内容进行比较。path<string> 序列化后的value将写入的文件。options<Object> 可选的配置选项。支持以下属性:serializers<Array> 一个同步函数数组,用于将value序列化为字符串。value会作为第一个序列化函数的唯一参数传入。每个序列化函数的返回值会作为下一个序列化函数的输入。一旦所有序列化器运行完毕,生成的值会被强制转换为字符串。默认: 如果未提供序列化器,则使用测试运行器的默认序列化器。
此函数将 value 序列化并写入由 path 指定的文件。
【This function serializes value and writes it to the file specified by path.】
test('snapshot test with default serialization', (t) => {
t.assert.fileSnapshot({ value1: 1, value2: 2 }, './snapshots/snapshot.json');
});
该函数与 context.assert.snapshot() 在以下方面有所不同:
【This function differs from context.assert.snapshot() in the following ways:】
- 快照文件路径由用户显式提供。
- 每个快照文件仅限包含一个快照值。
- 测试运行器不会执行额外的转义处理。
这些差异使快照文件能够更好地支持诸如语法高亮等功能。
【These differences allow snapshot files to better support features such as syntax highlighting.】
context.assert.snapshot(value[, options])#>
value<any> 要序列化为字符串的值。如果 Node.js 是使用--test-update-snapshots标志启动的,则序列化的值会写入快照文件。否则,序列化的值会与现有快照文件中的相应值进行比较。options<Object> 可选的配置选项。支持以下属性:serializers<Array> 一个同步函数数组,用于将value序列化为字符串。value会作为第一个序列化函数的唯一参数传入。每个序列化函数的返回值会作为输入传递给下一个序列化函数。一旦所有序列化函数运行完毕,结果值会被强制转换为字符串。默认值: 如果没有提供序列化函数,则使用测试运行器的默认序列化函数。
此函数实现快照测试的断言。
【This function implements assertions for snapshot testing.】
test('snapshot test with default serialization', (t) => {
t.assert.snapshot({ value1: 1, value2: 2 });
});
test('snapshot test with custom serialization', (t) => {
t.assert.snapshot({ value3: 3, value4: 4 }, {
serializers: [(value) => JSON.stringify(value)],
});
});
context.diagnostic(message)#>
message<string> 要报告的信息。
此函数用于将诊断信息写入输出。任何诊断信息都会包含在测试结果的末尾。此函数不返回值。
【This function is used to write diagnostics to the output. Any diagnostic information is included at the end of the test's results. This function does not return a value.】
test('top level test', (t) => {
t.diagnostic('A diagnostic message');
});
context.filePath#>
创建当前测试的测试文件的绝对路径。如果测试文件导入了生成测试的其他模块,导入的测试将返回根测试文件的路径。
【The absolute path of the test file that created the current test. If a test file imports additional modules that generate tests, the imported tests will return the path of the root test file.】
context.fullName#>
测试的名称及其每个祖级名称,用 > 分隔。
【The name of the test and each of its ancestors, separated by >.】
context.name#>
测试名称。
【The name of the test.】
context.plan(count[,options])#>
此函数用于设置测试中预期运行的断言和子测试的数量。如果实际运行的断言和子测试数量与预期数量不符,测试将会失败。
【This function is used to set the number of assertions and subtests that are expected to run within the test. If the number of assertions and subtests that run does not match the expected count, the test will fail.】
注意:为了确保断言被跟踪,必须使用
t.assert,而不能直接使用assert。
test('top level test', (t) => {
t.plan(2);
t.assert.ok('some relevant assertion here');
t.test('subtest', () => {});
});
在处理异步代码时,可以使用 plan 函数来确保运行正确数量的断言:
【When working with asynchronous code, the plan function can be used to ensure that the
correct number of assertions are run:】
test('planning with streams', (t, done) => {
function* generate() {
yield 'a';
yield 'b';
yield 'c';
}
const expected = ['a', 'b', 'c'];
t.plan(expected.length);
const stream = Readable.from(generate());
stream.on('data', (chunk) => {
t.assert.strictEqual(chunk, expected.shift());
});
stream.on('end', () => {
done();
});
});
使用 wait 选项时,你可以控制测试等待预期断言的时间。例如,设置最大等待时间可以确保测试在指定的时间范围内等待异步断言完成:
【When using the wait option, you can control how long the test will wait for the expected assertions.
For example, setting a maximum wait time ensures that the test will wait for asynchronous assertions
to complete within the specified timeframe:】
test('plan with wait: 2000 waits for async assertions', (t) => {
t.plan(1, { wait: 2000 }); // Waits for up to 2 seconds for the assertion to complete.
const asyncActivity = () => {
setTimeout(() => {
t.assert.ok(true, 'Async assertion completed within the wait time');
}, 1000); // Completes after 1 second, within the 2-second wait time.
};
asyncActivity(); // The test will pass because the assertion is completed in time.
});
注意:如果指定了 wait 超时,只有在测试函数执行完毕后,才会开始倒计时。
【Note: If a wait timeout is specified, it begins counting down only after the test function finishes executing.】
context.runOnly(shouldRunOnlyTests)#>
shouldRunOnlyTests<boolean> 是否仅运行only测试。
如果 shouldRunOnlyTests 为真,测试上下文将只运行设置了 only 选项的测试。否则,将运行所有测试。如果 Node.js 不是使用 --test-only 命令行选项启动的,则该函数无任何操作。
【If shouldRunOnlyTests is truthy, the test context will only run tests that
have the only option set. Otherwise, all tests are run. If Node.js was not
started with the --test-only command-line option, this function is a
no-op.】
test('top level test', (t) => {
// The test context can be set to run subtests with the 'only' option.
t.runOnly(true);
return Promise.all([
t.test('this subtest is now skipped'),
t.test('this subtest is run', { only: true }),
]);
});
context.signal#>
- 类型: <AbortSignal>
可用于在测试中止时中止测试子任务。
【Can be used to abort test subtasks when the test has been aborted.】
test('top level test', async (t) => {
await fetch('some/uri', { signal: t.signal });
});
context.skip([message])#>
message<string> 可选的跳过消息。
此函数会使测试的输出显示该测试已被跳过。如果提供了 message,它将包含在输出中。调用 skip() 不会终止测试函数的执行。此函数不返回任何值。
【This function causes the test's output to indicate the test as skipped. If
message is provided, it is included in the output. Calling skip() does
not terminate execution of the test function. This function does not return a
value.】
test('top level test', (t) => {
// Make sure to return here as well if the test contains additional logic.
t.skip('this is skipped');
});
context.todo([message])#>
message<string> 可选的TODO消息。
此函数会在测试输出中添加 TODO 指令。如果提供了 message,它将包含在输出中。调用 todo() 不会终止测试函数的执行。此函数不返回值。
【This function adds a TODO directive to the test's output. If message is
provided, it is included in the output. Calling todo() does not terminate
execution of the test function. This function does not return a value.】
test('top level test', (t) => {
// This test is marked as `TODO`
t.todo('this is a todo');
});
context.test([name][, options][, fn])#>
name<string> 子测试的名称,在报告测试结果时显示。默认值:fn的name属性,如果fn没有名称,则为'<anonymous>'。options<Object> 子测试的配置选项。支持以下属性:concurrency<number> | <boolean> | <null> 如果提供了一个数字,那么在应用线程中将会有相应数量的测试并行运行。如果为true,则会并行运行所有子测试。如果为false,则每次只运行一个测试。如果未指定,子测试将继承其父测试的该值。默认值:null。only<boolean> 如果为真,并且测试环境配置为只运行only测试,则将运行此测试。否则,该测试将被跳过。默认值:false。signal<AbortSignal> 允许中止正在进行的测试。skip<boolean> | <string> 如果为真,则跳过该测试。如果提供了字符串,该字符串将显示在测试结果中作为跳过测试的原因。默认值:false。todo<boolean> | <string> 如果为真值,则将测试标记为TODO。如果提供了字符串,则该字符串将在测试结果中显示,作为测试为TODO的原因。默认值:false。timeout<number> 测试将在指定毫秒数后失败。如果未指定,子测试将继承父测试的此值。 默认值:Infinity。plan<number> 预期在测试中运行的断言和子测试的数量。如果测试中运行的断言数量与计划中指定的数量不符,测试将会失败。默认值:undefined。
fn<Function> | <AsyncFunction> 被测试的函数。该函数的第一个参数是一个TestContext对象。如果测试使用回调函数,回调函数作为第二个参数传入。**默认值:**一个空操作函数。- 返回:<Promise> 在测试完成后以
undefined状态兑现。
此函数用于在当前测试下创建子测试。此函数的行为与顶层 test() 函数相同。
【This function is used to create subtests under the current test. This function
behaves in the same fashion as the top level test() function.】
test('top level test', async (t) => {
await t.test(
'This is a subtest',
{ only: false, skip: false, concurrency: 1, todo: false, plan: 1 },
(t) => {
t.assert.ok('some relevant assertion here');
},
);
});
context.waitFor(condition[, options])#>
condition<Function> | <AsyncFunction> 一个断言函数,会定期调用,直到成功完成或达到定义的轮询超时时间。成功完成的定义是不抛出错误或拒绝。该函数不接受任何参数,并且可以返回任何值。options<Object> 可选的轮询操作配置对象。支持以下属性:- 返回值: <Promise> 返回
condition返回的值。
该方法会轮询 condition 函数,直到该函数成功返回或操作超时。
【This method polls a condition function until that function either returns
successfully or the operation times out.】
类:SuiteContext#>
【Class: SuiteContext】
每个测试套件函数都会传入一个 SuiteContext 实例,以便与测试运行器进行交互。然而,SuiteContext 构造函数并未作为 API 的一部分公开。
【An instance of SuiteContext is passed to each suite function in order to
interact with the test runner. However, the SuiteContext constructor is not
exposed as part of the API.】
context.filePath#>
创建当前测试套件的测试文件的绝对路径。如果一个测试文件导入了生成套件的其他模块,导入的套件将返回根测试文件的路径。
【The absolute path of the test file that created the current suite. If a test file imports additional modules that generate suites, the imported suites will return the path of the root test file.】
context.name#>
套件名称。
【The name of the suite.】
context.signal#>
- 类型: <AbortSignal>
可用于在测试中止时中止测试子任务。
【Can be used to abort test subtasks when the test has been aborted.】