Node.js v18.15.0 文档


目录

test 测试#

中英对照

稳定性: 1 - 实验

源代码: lib/test.js

node:test 模块有助于创建 JavaScript 测试。 要访问它:

import test from 'node:test';const test = require('node:test');

此模块仅在 node: 协议下可用。 以下将不起作用:

import test from 'test';const test = require('test');

通过 test 模块创建的测试由单个函数组成,该函数以三种方式之一进行处理:

  1. 同步的函数,如果抛出异常则认为失败,否则认为通过。
  2. 返回 Promise 的函数,如果 Promise 拒绝,则认为该函数失败,如果 Promise 解决,则认为该函数通过。
  3. 接收回调函数的函数。 如果回调接收到任何真值作为其第一个参数,则认为测试失败。 如果非真值作为第一个参数传给回调,则认为测试通过。 如果测试函数接收到回调函数并且还返回 Promise,则测试将失败。

以下示例说明了如何使用 test 模块编写测试。

test('synchronous passing test', (t) => {
  // 此测试通过了,因为它没有抛出异常。
  assert.strictEqual(1, 1);
});

test('synchronous failing test', (t) => {
  // 此测试失败,因为它抛出了异常。
  assert.strictEqual(1, 2);
});

test('asynchronous passing test', async (t) => {
  // 此测试通过了,
  // 因为异步函数返回的 Promise 没有被拒绝。
  assert.strictEqual(1, 1);
});

test('asynchronous failing test', async (t) => {
  // 此测试失败,
  // 因为异步函数返回的 Promise 被拒绝。
  assert.strictEqual(1, 2);
});

test('failing test using Promises', (t) => {
  // Promise 也可以直接使用。
  return new Promise((resolve, reject) => {
    setImmediate(() => {
      reject(new Error('this will cause the test to fail'));
    });
  });
});

test('callback passing test', (t, done) => {
  // done() 是回调函数。
  // 当 setImmediate() 运行时,它调用 done() 不带参数。
  setImmediate(done);
});

test('callback failing test', (t, done) => {
  // 当 setImmediate() 运行时,
  // 使用 Error 对象调用 done() 并且测试失败。
  setImmediate(() => {
    done(new Error('callback failure'));
  });
});

如果任何测试失败,则进程退出代码设置为 1

子测试#

中英对照

测试上下文的 test() 方法允许创建子测试。 此方法的行为与顶层 test() 函数相同。 以下示例演示了如何创建具有两个子测试的顶层测试。

test('top level test', async (t) => {
  await t.test('subtest 1', (t) => {
    assert.strictEqual(1, 1);
  });

  await t.test('subtest 2', (t) => {
    assert.strictEqual(2, 2);
  });
});

在本示例中,await 用于确保两个子测试均已完成。 这是必要的,因为父测试不会等待子测试完成。 当父测试完成时仍然未完成的任何子测试将被取消并视为失败。 任何子测试失败都会导致父测试失败。

跳过测试#

中英对照

通过将 skip 选项传递给测试,或通过调用测试上下文的 skip() 方法,可以跳过单个测试,如下例所示。

// 使用了跳过选项,但没有提供任何消息。
test('skip option', { skip: true }, (t) => {
  // 这段代码永远不会被执行。
});

// 使用了跳过选项,并提供了一条消息。
test('skip option with message', { skip: 'this is skipped' }, (t) => {
  // 这段代码永远不会被执行。
});

test('skip() method', (t) => {
  // 如果测试包含额外的逻辑,则务必返回这里。
  t.skip();
});

test('skip() method with message', (t) => {
  // 如果测试包含额外的逻辑,则务必返回这里。
  t.skip('this is skipped');
});

describe/it 语法#

中英对照

运行测试也可以使用 describe 来声明套件和 it 来声明测试。 套件用于将相关测试组织和分组在一起。 ittest 的别名,除了没有通过测试上下文,因为嵌套是使用套件完成的。

describe('A thing', () => {
  it('should work', () => {
    assert.strictEqual(1, 1);
  });

  it('should be ok', () => {
    assert.strictEqual(2, 2);
  });

  describe('a nested thing', () => {
    it('should work', () => {
      assert.strictEqual(3, 3);
    });
  });
});

describeit 是从 node:test 模块导入的。

import { describe, it } from 'node:test';const { describe, it } = require('node:test');

only 测试#

中英对照

如果 Node.js 使用 --test-only 命令行选项启动,则可以通过将 only 选项传给应该运行的测试来跳过除选定子集之外的所有顶层测试。 当运行带有 only 选项集的测试时,所有子测试也会运行。 测试上下文的 runOnly() 方法可用于在子测试级别实现相同的行为。

// 假设 Node.js 使用 --test-only 命令行选项运行。
// 设置了 'only' 选项,因此运行此测试。
test('this test is run', { only: true }, async (t) => {
  // 在此测试中,默认运行所有子测试。
  await t.test('running subtest');

  // 可以使用 'only' 选项更新测试上下文以运行子测试。
  t.runOnly(true);
  await t.test('this subtest is now skipped');
  await t.test('this subtest is run', { only: true });

  // 切换上下文以执行所有测试。
  t.runOnly(false);
  await t.test('this subtest is now run');

  // 显式地不要运行这些测试。
  await t.test('skipped subtest 3', { only: false });
  await t.test('skipped subtest 4', { skip: true });
});

// 未设置 'only' 选项,因此跳过此测试。
test('this test is not run', () => {
  // 此代码未运行。
  throw new Error('fail');
});

Filtering tests by name#

中英对照

--test-name-pattern 命令行选项可用于仅运行名称与提供的模式匹配的测试。 测试名称模式被解释为 JavaScript 正则表达式。 可以多次指定 --test-name-pattern 选项以运行嵌套测试。 对于执行的每个测试,也会运行任何相应的测试挂钩,例如 beforeEach()

给定以下测试文件,使用 --test-name-pattern="test [1-3]" 选项启动 Node.js 将导致测试运行程序执行 test 1test 2test 3。 如果 test 1 不匹配测试名称模式,那么它的子测试将不会执行,尽管匹配模式。 同一组测试也可以通过多次传递 --test-name-pattern 来执行(例如 --test-name-pattern="test 1"--test-name-pattern="test 2" 等)。

test('test 1', async (t) => {
  await t.test('test 2');
  await t.test('test 3');
});

test('Test 4', async (t) => {
  await t.test('Test 5');
  await t.test('test 6');
});

也可以使用正则表达式文字指定测试名称模式。 这允许使用正则表达式标志。 在前面的示例中,以 --test-name-pattern="/test [4-5]/i" 开头的 Node.js 将匹配 Test 4Test 5,因为该模式不区分大小写。

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

无关的异步活动#

中英对照

一旦测试函数完成执行,将在保持测试顺序的同时尽快报告结果。 但是,测试函数可能会生成比测试本身寿命更长的异步活动。 测试运行器处理此类活动,但不会延迟报告测试结果以适应它。

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

第二个 setImmediate() 创建了 uncaughtException 事件。 源自已完成测试的 uncaughtExceptionunhandledRejection 事件被 test 模块标记为失败,并由 <TestsStream> 在顶层报告为诊断警告。

test('a test that creates asynchronous activity', (t) => {
  setImmediate(() => {
    t.test('subtest that is created too late', (t) => {
      throw new Error('error1');
    });
  });

  setImmediate(() => {
    throw new Error('error2');
  });

  // 此行之后测试结束。
});

Watch mode#

中英对照

稳定性: 1 - 实验

Node.js 测试运行器支持通过传递 --watch 标志以监视模式运行:

node --test --watch

在监视模式下,测试运行器将监视测试文件及其依赖项的更改。 当检测到变化时,测试运行器将重新运行受变化影响的测试。 测试运行器将继续运行直到进程终止。

从命令行运行测试#

中英对照

可以通过传入 --test 标志从命令行调用 Node.js 测试运行程序:

node --test

默认情况下,Node.js 将递归搜索当前目录以查找匹配特定命名约定的 JavaScript 源文件。 匹配文件作为测试文件执行。 有关预期测试文件命名约定和行为的更多信息可以在测试运行器执行模型章节中找到。

或者,可以提供一个或多个路径作为 Node.js 命令的最终参数,如下所示。

node --test test1.js test2.mjs custom_test_dir/

在本例中,测试运行程序将执行文件 test1.jstest2.mjs。 测试运行器还将递归搜索 custom_test_dir/ 目录以查找要执行的测试文件。

测试运行器执行模型#

中英对照

当搜索要执行的测试文件时,测试运行器的行为如下:

  • 执行用户显式提供的任何文件。
  • 如果用户没有显式地指定任何路径,则递归搜索当前工作目录中指定的文件,如以下步骤所示。
  • 除非用户显式地提供,否则跳过 node_modules 目录。
  • 如果遇到名为 test 的目录,则测试运行程序将递归搜索所有 .js.cjs.mjs 文件。 所有这些文件都被视为测试文件,不需要匹配下面详述的特定命名约定。 这是为了适应将所有测试放在单个 test 目录中的项目。
  • 在所有其他目录中,匹配以下模式的 .js.cjs.mjs 文件被视为测试文件:
    • ^test$ - 基本名称为字符串 'test' 的文件。 示例:test.jstest.cjstest.mjs
    • ^test-.+ - 基本名称以字符串 'test-' 开头,后跟一个或多个字符的文件。 示例:test-example.jstest-another-example.mjs
    • .+[\.\-\_]test$ - 基本名称以 .test-test_test 结尾的文件,前面有一个或多个字符。 示例:example.test.jsexample-test.cjsexample_test.mjs
    • Node.js 理解的其他文件类型,例如 .node.json,不会由测试运行程序自动执行,但如果在命令行上显式地提供,则支持。

每个匹配的测试文件都在单独的子进程中执行。 如果子进程以退出代码 0 结束,则认为测试通过。 否则,认为测试失败。 测试文件必须是 Node.js 可执行文件,但不需要在内部使用 node:test 模块。

Collecting code coverage#

中英对照

当 Node.js 以 --experimental-test-coverage 命令行标志启动时,代码覆盖率将被收集并在所有测试完成后报告统计信息。 如果使用 NODE_V8_COVERAGE 环境变量指定代码覆盖目录,则生成的 V8 覆盖文件写入该目录。 node_modules/ 目录中的 Node.js 核心模块和文件未包含在覆盖率报告中。 如果启用覆盖,覆盖报告将通过 'test:coverage' 事件发送给任何 测试报告者

可以使用以下注释语法在一系列行上禁用覆盖:

/* 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 */

也可以针对指定行数禁用覆盖。 在指定的行数之后,将自动重新启用覆盖。 如果未明确提供行数,则忽略单行。

/* 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');
}

测试运行器的代码覆盖功能有以下限制,将在未来的 Node.js 版本中解决:

  • 虽然为子进程收集了覆盖率数据,但此信息未包含在覆盖率报告中。 因为命令行测试运行器使用子进程来执行测试文件,所以不能与 --experimental-test-coverage 一起使用。
  • 不支持源映射。
  • 不支持从覆盖率报告中排除特定文件或目录。

Mocking#

中英对照

node:test 模块支持通过顶级 mock 对象在测试期间进行模拟。 以下示例创建了一个对将两个数字相加的函数的监视。 间谍随后被用来断言该函数已按预期调用。

import assert from 'node:assert';
import { mock, test } from 'node:test';

test('spies on a function', () => {
  const sum = mock.fn((a, b) => {
    return a + b;
  });

  assert.strictEqual(sum.mock.calls.length, 0);
  assert.strictEqual(sum(3, 4), 7);
  assert.strictEqual(sum.mock.calls.length, 1);

  const call = sum.mock.calls[0];
  assert.deepStrictEqual(call.arguments, [3, 4]);
  assert.strictEqual(call.result, 7);
  assert.strictEqual(call.error, undefined);

  // 重置全局跟踪的模拟。
  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.calls.length, 0);
  assert.strictEqual(sum(3, 4), 7);
  assert.strictEqual(sum.mock.calls.length, 1);

  const call = sum.mock.calls[0];
  assert.deepStrictEqual(call.arguments, [3, 4]);
  assert.strictEqual(call.result, 7);
  assert.strictEqual(call.error, undefined);

  // 重置全局跟踪的模拟。
  mock.reset();
});

相同的模拟功能也暴露在每个测试的 TestContext 对象上。 以下示例使用 TestContext 上公开的 API 创建对象方法的侦听器。 通过测试上下文进行模拟的好处是,测试运行器将在测试完成后自动恢复所有模拟功能。

test('spies on an object method', (t) => {
  const number = {
    value: 5,
    add(a) {
      return this.value + a;
    },
  };

  t.mock.method(number, 'add');
  assert.strictEqual(number.add.mock.calls.length, 0);
  assert.strictEqual(number.add(3), 8);
  assert.strictEqual(number.add.mock.calls.length, 1);

  const call = number.add.mock.calls[0];

  assert.deepStrictEqual(call.arguments, [3]);
  assert.strictEqual(call.result, 8);
  assert.strictEqual(call.target, undefined);
  assert.strictEqual(call.this, number);
});

Test reporters#

中英对照

node:test 模块支持传递 --test-reporter 标志,以便测试运行器使用特定的报告程序。

支持以下内置报告器:

  • tap tap 报告器是测试运行程序使用的默认报告器。 它以 TAP 格式输出测试结果。

  • spec spec 报告器以人类可读的格式输出测试结果。

  • dot dot 报告器以紧凑的格式输出测试结果,其中每个通过的测试用一个 . 表示,每个失败的测试用一个 X 表示。

Custom reporters#

中英对照

--test-reporter 可用于指定自定义报告程序的路径。 自定义报告器是一个模块,它导出 stream.compose 接受的值。 报告者应该转换 <TestsStream> 发出的事件

使用 <stream.Transform> 的自定义报告器示例:

import { Transform } from 'node:stream';

const customReporter = new Transform({
  writableObjectMode: true,
  transform(event, encoding, callback) {
    switch (event.type) {
      case 'test:start':
        callback(null, `test ${event.data.name} started`);
        break;
      case 'test:pass':
        callback(null, `test ${event.data.name} passed`);
        break;
      case 'test:fail':
        callback(null, `test ${event.data.name} failed`);
        break;
      case 'test:plan':
        callback(null, 'test plan');
        break;
      case 'test:diagnostic':
        callback(null, event.data.message);
        break;
    }
  },
});

export default customReporter;const { Transform } = require('node:stream');

const customReporter = new Transform({
  writableObjectMode: true,
  transform(event, encoding, callback) {
    switch (event.type) {
      case 'test:start':
        callback(null, `test ${event.data.name} started`);
        break;
      case 'test:pass':
        callback(null, `test ${event.data.name} passed`);
        break;
      case 'test:fail':
        callback(null, `test ${event.data.name} failed`);
        break;
      case 'test:plan':
        callback(null, 'test plan');
        break;
      case 'test:diagnostic':
        callback(null, event.data.message);
        break;
    }
  },
});

module.exports = customReporter;

使用生成器函数的自定义报告器示例:

export default async function * customReporter(source) {
  for await (const event of source) {
    switch (event.type) {
      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';
        break;
      case 'test:diagnostic':
        yield `${event.data.message}\n`;
        break;
    }
  }
}module.exports = async function * customReporter(source) {
  for await (const event of source) {
    switch (event.type) {
      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':
        yield `${event.data.message}\n`;
        break;
    }
  }
};

提供给 --test-reporter 的值应该是一个字符串,就像在 JavaScript 代码中的 import() 中使用的那样。

Multiple reporters#

中英对照

可以多次指定 --test-reporter 标志,以多种格式报告测试结果。 在这种情况下,需要使用 --test-reporter-destination 为每个报告者指定目的地。 目标可以是 stdoutstderr 或文件路径。 报告器和目的地根据指定的顺序配对。

在下面的例子中,spec 报告器将输出到 stdoutdot 报告器将输出到 file.txt

node --test-reporter=spec --test-reporter=dot --test-reporter-destination=stdout --test-reporter-destination=file.txt

当指定单个报告者时,目的地将默认为 stdout,除非明确提供目的地。

run([options])#

中英对照

  • options <Object> 运行测试的配置选项。 支持以下属性:
    • concurrency <number> | <boolean> 如果提供了一个数字,那么那么多文件将并行运行。 如果是真的,它将并行运行 (cpu cores - 1) 文件。 如果是虚假的,它一次只会运行一个文件。 如果未指定,则子测试从其父测试继承此值。 默认值: true
    • files: <Array> 包含要运行的文件列表的数组。 默认来自 测试运行器执行模型 的匹配文件。
    • setup <Function> 接受 TestsStream 实例并可用于在运行任何测试之前设置侦听器的函数。 默认值: undefined
    • signal <AbortSignal> 允许中止正在进行的测试执行。
    • timeout <number> 测试执行将在几毫秒后失败。 如果未指定,则子测试从其父测试继承此值。 默认值: Infinity
    • inspectPort <number> | <Function> 设置测试子进程的检查器端口。 这可以是数字,也可以是不带参数并返回数字的函数。 如果提供了一个空值,每个进程都有自己的端口,从主进程的 process.debugPort 递增。 默认值: undefined
  • 返回: <TestsStream>
run({ files: [path.resolve('./tests/test.js')] })
  .pipe(process.stdout);

test([name][, options][, fn])#

中英对照

  • name <string> 测试的名称,报告测试结果时显示。 默认值: The name fn 的属性,如果 fn 没有名称,则为 '<anonymous>'
  • options <Object> 测试的配置选项。 支持以下属性:
    • concurrency <number> | <boolean> 如果提供了一个数字,则多个测试将并行运行。 如果为真,则它将并行运行 (cpu 内核 - 1) 个测试。 对于子测试,它将是 Infinity 并行测试。 如果不为真,则一次只会运行一个测试。 如果未指定,则子测试从其父测试继承此值。 默认值: false
    • only <boolean> 如果为真,并且测试上下文配置为运行 only 测试,则将运行此测试。 否则跳过测试。 默认值: false
    • signal <AbortSignal> 允许中止正在进行的测试。
    • skip <boolean> | <string> 如果为真,则跳过测试。 如果提供了字符串,则该字符串将作为跳过测试的原因显示在测试结果中。 默认值: false
    • todo <boolean> | <string> 如果为真,则测试标记为 TODO。 如果提供了字符串,则该字符串会显示在测试结果中作为测试为 TODO 的原因。 默认值: false
    • timeout <number> 测试失败的毫秒数。 如果未指定,则子测试从其父测试继承此值。 默认值: Infinity
  • fn <Function> | <AsyncFunction> 被测试的函数。 此函数的第一个参数是 TestContext 对象。 如果测试使用回调,则回调函数作为第二个参数传入。 默认值: 无操作的函数。
  • 返回: <Promise> 测试完成后使用 undefined 解决。

test() 函数是从 test 模块导入的值。 每次调用此函数都会向 <TestsStream> 报告测试。

传给 fn 参数的 TestContext 对象可用于执行与当前测试相关的操作。 示例包括跳过测试、添加额外的诊断信息或创建子测试。

test() 返回 Promise,一旦测试完成就解决。 返回值通常可以被顶层测试丢弃。 但是,应该使用子测试的返回值来防止父测试先完成并取消子测试,如下例所示。

test('top level test', async (t) => {
  // 如果在下一行删除了 'await',
  // 则以下子测试中的 setTimeout() 将导致它的父测试寿命超过其父测试。
  // 一旦父测试完成,则它将取消所有未完成的子测试。
  await t.test('longer running subtest', async (t) => {
    return new Promise((resolve, reject) => {
      setTimeout(resolve, 1000);
    });
  });
});

如果完成时间超过 timeout 毫秒,则可以使用 timeout 选项使测试失败。 但是,它不是取消测试的可靠机制,因为正在运行的测试可能会阻塞应用程序线程,从而阻止预定的取消。

describe([name][, options][, fn])#

中英对照

  • name <string> 套件名称,报告测试结果时显示。 默认值: The name fn 的属性,如果 fn 没有名称,则为 '<anonymous>'
  • options <Object> 套件的配置选项。 支持与 test([name][, options][, fn]) 相同的选项。
  • fn <Function> | <AsyncFunction> 套件下的函数声明所有子测试和子套件。 此函数的第一个参数是 SuiteContext 对象。 默认值: 无操作的函数。
  • 返回: undefined

node:test 模块导入的 describe() 函数。 每次调用此函数都会创建一个子测试。 调用顶级 describe 函数后,所有顶级测试和套件都将执行。

describe.skip([name][, options][, fn])#

中英对照

跳过套件的简写,与 describe([name], { skip: true }[, fn]) 相同。

describe.todo([name][, options][, fn])#

中英对照

将套件标记为 TODO 的简写,与 describe([name], { todo: true }[, fn]) 相同。

describe.only([name][, options][, fn])#

中英对照

将套件标记为 only 的简写,与 describe([name], { only: true }[, fn]) 相同。

it([name][, options][, fn])#

中英对照

  • name <string> 测试的名称,报告测试结果时显示。 默认值: The name fn 的属性,如果 fn 没有名称,则为 '<anonymous>'
  • options <Object> 套件的配置选项。 支持与 test([name][, options][, fn]) 相同的选项。
  • fn <Function> | <AsyncFunction> 被测试的函数。 如果测试使用回调,则回调函数作为参数传入。 默认值: 无操作的函数。
  • 返回: undefined

it() 函数是从 node:test 模块导入的值。

it.skip([name][, options][, fn])#

中英对照

跳过测试的简写,与 it([name], { skip: true }[, fn]) 相同。

it.todo([name][, options][, fn])#

中英对照

将测试标记为 TODO 的简写,与 it([name], { todo: true }[, fn]) 相同。

it.only([name][, options][, fn])#

中英对照

将测试标记为 only 的简写,与 it([name], { only: true }[, fn]) 相同。

before([fn][, options])#

中英对照

  • fn <Function> | <AsyncFunction> 钩子函数。 如果钩子使用回调,则回调函数作为第二个参数传入。 默认值: 无操作的函数。
  • options <Object> 钩子的配置选项。 支持以下属性:
    • signal <AbortSignal> 允许中止正在进行的钩子。
    • timeout <number> 钩子会在几毫秒后失败。 如果未指定,则子测试从其父测试继承此值。 默认值: Infinity

此函数用于在运行套件之前创建一个钩子运行。

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

该函数用于创建一个运行套件后运行的钩子。

describe('tests', async () => {
  after(() => console.log('finished running tests'));
  it('is a subtest', () => {
    assert.ok('some relevant assertion here');
  });
});

beforeEach([fn][, options])#

中英对照

  • fn <Function> | <AsyncFunction> 钩子函数。 如果钩子使用回调,则回调函数作为第二个参数传入。 默认值: 无操作的函数。
  • options <Object> 钩子的配置选项。 支持以下属性:
    • signal <AbortSignal> 允许中止正在进行的钩子。
    • timeout <number> 钩子会在几毫秒后失败。 如果未指定,则子测试从其父测试继承此值。 默认值: Infinity

此函数用于创建一个在当前套件的每个子测试之前运行的钩子。

describe('tests', async () => {
  beforeEach(() => t.diagnostic('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

此函数用于创建一个在当前测试的每个子测试之后运行的钩子。

describe('tests', async () => {
  afterEach(() => t.diagnostic('about to run a test'));
  it('is a subtest', () => {
    assert.ok('some relevant assertion here');
  });
});

MockFunctionContext#

中英对照

MockFunctionContext 类用于检查或操纵通过 MockTracker API 创建的模拟的行为。

ctx.calls#

中英对照

返回用于跟踪模拟调用的内部数组副本的获取器。 数组中的每个条目都是一个具有以下属性的对象。

  • 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 是创建内部调用跟踪数组副本的获取器。

ctx.mockImplementation(implementation)#

中英对照

此函数用于更改现有模拟的行为。

以下示例使用 t.mock.fn() 创建模拟函数,调用模拟函数,然后将模拟实现更改为不同的函数。

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() 时它会使用的任何行为。

以下示例使用 t.mock.fn() 创建模拟函数,调用模拟函数,将模拟实现更改为下一次调用的不同函数,然后恢复其先前的行为。

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()#

中英对照

重置模拟函数的调用历史。

ctx.restore()#

中英对照

将模拟函数的实现重置为其原始行为。 调用此函数后仍然可以使用模拟。

MockTracker#

中英对照

MockTracker 类用于管理模拟功能。 测试运行器模块提供了一个顶级 mock 导出,它是一个 MockTracker 实例。 每个测试还通过测试上下文的 mock 属性提供自己的 MockTracker 实例。

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 的一个实例,可用于检查和更改模拟函数的行为。

此函数用于创建模拟函数。

以下示例创建了一个模拟函数,每次调用时计数器都会递增 1。 times 选项用于修改模拟行为,以便前两次调用将两个而不是一个添加到计数器。

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

mock.method(object, methodName[, implementation][, options])#

中英对照

  • object <Object> 其方法被模拟的对象。
  • methodName <string> | <symbol> 要模拟的 object 上的方法的标识符。 如果 object[methodName] 不是函数,则会抛出错误。
  • implementation <Function> | <AsyncFunction> 用作 object[methodName] 的模拟实现的可选函数。 默认值: object[methodName] 指定的原始方法。
  • options <Object> 模拟方法的可选配置选项。 支持以下属性:
    • getter <boolean> 如果为 trueobject[methodName] 被当作获取器。 此选项不能与 setter 选项一起使用。 默认值: false。
    • setter <boolean> 如果为 trueobject[methodName] 被视为设置器。 此选项不能与 getter 选项一起使用。 默认值: false。
    • times <integer> 模拟将使用 implementation 的行为的次数。 一旦被模拟的方法被调用了 times 次,它会自动恢复原来的行为。 此值必须是大于零的整数。 默认值: Infinity
  • 返回: <Proxy> 被模拟的方法。 模拟方法包含一个特殊的 mock 属性,它是 MockFunctionContext 的实例,可用于检查和更改模拟方法的行为。

此函数用于在现有对象方法上创建模拟。 以下示例演示了如何在现有对象方法上创建模拟。

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.calls.length, 0);
  assert.strictEqual(number.subtract(3), 2);
  assert.strictEqual(number.subtract.mock.calls.length, 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.reset()#

中英对照

此函数恢复了此 MockTracker 之前创建的所有模拟的默认行为,并解除了模拟与 MockTracker 实例的关联。 一旦解除关联,模拟仍然可以使用,但 MockTracker 实例不能再用于重置它们的行为或以其他方式与它们交互。

每次测试完成后,都会在测试上下文的 MockTracker 上调用此函数。 如果广泛使用全局 MockTracker,建议手动调用该函数。

mock.restoreAll()#

中英对照

此函数恢复此 MockTracker 之前创建的所有模拟的默认行为。 与 mock.reset() 不同,mock.restoreAll() 不会取消模拟与 MockTracker 实例的关联。

mock.setter(object, methodName[, implementation][, options])#

中英对照

此函数是 MockTracker.method 的语法糖,options.setter 设置为 true

TestsStream#

中英对照

成功调用 run() 方法将返回一个新的 <TestsStream> 对象,流式传输代表测试执行的一系列事件。 TestsStream 将按照测试定义的顺序发出事件

'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> 覆盖的函数百分比。
        • uncoveredLineNumbers <Array> 一个整数数组,表示未覆盖的行号。
      • 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> 测试的嵌套级别。

启用代码覆盖率并且所有测试都已完成时发出。

'test:diagnostic' 事件#

中英对照

调用 context.diagnostic 时触发。

'test:fail' 事件#

中英对照

测试失败时触发。

'test:pass' 事件#

中英对照

测试通过时触发。

'test:plan' 事件#

中英对照

当给定测试的所有子测试都完成时发出。

'test:start' 事件#

中英对照

测试开始时发出。

TestContext#

中英对照

TestContext 的实例被传给每个测试函数,以便与测试运行器交互。 但是,TestContext 构造函数没有作为 API 的一部分公开。

context.beforeEach([fn][, options])#

中英对照

  • fn <Function> | <AsyncFunction> 钩子函数。 此函数的第一个参数是 TestContext 对象。 如果钩子使用回调,则回调函数作为第二个参数传入。 默认值: 无操作的函数。
  • options <Object> 钩子的配置选项。 支持以下属性:
    • signal <AbortSignal> 允许中止正在进行的钩子。
    • timeout <number> 钩子会在几毫秒后失败。 如果未指定,则子测试从其父测试继承此值。 默认值: Infinity

此函数用于创建一个在当前测试的每个子测试之前运行的钩子。

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

此函数用于创建一个在当前测试完成后运行的钩子。

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

此函数用于创建一个在当前测试的每个子测试之后运行的钩子。

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.diagnostic(message)#

中英对照

  • message <string> 要报告的消息。

此函数用于将诊断写入输出。 任何诊断信息都包含在测试结果的末尾。 此函数不返回值。

test('top level test', (t) => {
  t.diagnostic('A diagnostic message');
});

context.name#

中英对照

测试名称。

context.runOnly(shouldRunOnlyTests)#

中英对照

  • shouldRunOnlyTests <boolean> 是否运行 only 测试。

如果 shouldRunOnlyTests 为真,则测试上下文将只运行设置了 only 选项的测试。 否则,将运行所有测试。 如果 Node.js 不是使用 --test-only 命令行选项启动的,则此函数是无操作的。

test('top level test', (t) => {
  // 可以将测试上下文设置为使用 'only' 选项运行子测试。
  t.runOnly(true);
  return Promise.all([
    t.test('this subtest is now skipped'),
    t.test('this subtest is run', { only: true }),
  ]);
});

context.signal#

中英对照

  • <AbortSignal> 可用于在测试中止时中止测试子任务。
test('top level test', async (t) => {
  await fetch('some/uri', { signal: t.signal });
});

context.skip([message])#

中英对照

  • message <string> 可选的跳过消息。

此函数使测试的输出指示测试已跳过。 如果提供了 message,它会包含在输出中。 调用 skip() 不会终止测试函数的执行。 此函数不返回值。

test('top level test', (t) => {
  // 如果测试包含额外的逻辑,则务必返回这里。
  t.skip('this is skipped');
});

context.todo([message])#

中英对照

  • message <string> 可选的 TODO 消息。

此函数将 TODO 指令添加到测试的输出中。 如果提供了 message,它会包含在输出中。 调用 todo() 不会终止测试函数的执行。 此函数不返回值。

test('top level test', (t) => {
  // 此测试标记为 `TODO`
  t.todo('this is a todo');
});

context.test([name][, options][, fn])#

中英对照

  • name <string> 子测试的名称,在报告测试结果时显示。 默认值: The name fn 的属性,如果 fn 没有名称,则为 '<anonymous>'
  • options <Object> 子测试的配置选项。 支持以下属性:
    • concurrency <number> 可同时运行的测试数。 如果未指定,则子测试从其父测试继承此值。 默认值: 1
    • only <boolean> 如果为真,并且测试上下文配置为运行 only 测试,则将运行此测试。 否则跳过测试。 默认值: false
    • signal <AbortSignal> 允许中止正在进行的测试。
    • skip <boolean> | <string> 如果为真,则跳过测试。 如果提供了字符串,则该字符串将作为跳过测试的原因显示在测试结果中。 默认值: false
    • todo <boolean> | <string> 如果为真,则测试标记为 TODO。 如果提供了字符串,则该字符串会显示在测试结果中作为测试为 TODO 的原因。 默认值: false
    • timeout <number> 测试失败的毫秒数。 如果未指定,则子测试从其父测试继承此值。 默认值: Infinity
  • fn <Function> | <AsyncFunction> 被测试的函数。 此函数的第一个参数是 TestContext 对象。 如果测试使用回调,则回调函数作为第二个参数传入。 默认值: 无操作的函数。
  • 返回: <Promise> 测试完成后使用 undefined 解决。

此函数用于在当前测试下创建子测试。 此函数的行为方式与顶层的 test() 函数相同。

test('top level test', async (t) => {
  await t.test(
    'This is a subtest',
    { only: false, skip: false, concurrency: 1, todo: false },
    (t) => {
      assert.ok('some relevant assertion here');
    },
  );
});

SuiteContext#

中英对照

SuiteContext 的实例被传给每个套件函数,以便与测试运行器进行交互。 但是,SuiteContext 构造函数没有作为 API 的一部分公开。

context.name#

中英对照

套件名称。

context.signal#

中英对照

  • <AbortSignal> 可用于在测试中止时中止测试子任务。
返回顶部