Node.js v18.18.2 文档


目录

单元测试#

稳定性: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) => {
  // 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 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

子测试#

测试上下文的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()方法,可以跳过各个测试,如以下示例所示。

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

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() 方法可用于在子测试级别实现相同的行为。

// Assume Node.js is run with the --test-only command-line option.
// The 'only' option is set, so this test is 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');
}); 

按名称过滤测试#

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

  // The test finishes after this line.
}); 

观察模式#

稳定性: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模块。

每个测试文件都像常规脚本一样执行。也就是说,如果测试文件本身使用node:test定义测试,则所有这些测试都将在单个应用程序线程中执行,无论 test()concurrency选项的值如何。

收集代码覆盖率#

当 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 版本中解决:

  • 不支持源映射。
  • 不支持从覆盖率报告中排除特定文件或目录。

Mock#

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

  // 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.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);

  // Reset the globally tracked mocks.
  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);
}); 

测试报告器#

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

支持以下内置报告器:

  • tap tap 报告器以TAP格式输出测试结果。

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

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

stdoutTTY时,默认使用spec报告器。否则,默认使用tap报告器。

记者可通过node:test/reporters模块获得:

import { tap, spec, dot } from 'node:test/reporters';const { tap, spec, dot } = require('node:test/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;
      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:start':
        callback(null, `test ${event.data.name} started`);
        break;
      case 'test:pass':
        callback(null, `test ${event.data.name} passed`);
        break;
      case 'test:fail':
        callback(null, `test ${event.data.name} failed`);
        break;
      case 'test:plan':
        callback(null, 'test plan');
        break;
      case 'test:diagnostic':
        callback(null, event.data.message);
        break;
      case 'test:coverage': {
        const { totalLineCount } = event.data.summary.totals;
        callback(null, `total line count: ${totalLineCount}\n`);
        break;
      }
    }
  },
});

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;
      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: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;
      case 'test:coverage': {
        const { totalLineCount } = event.data.summary.totals;
        yield `total line count: ${totalLineCount}\n`;
        break;
      }
    }
  }
};

提供给--test-reporter的值应该是一个类似于 JavaScript 代码中import()中使用的字符串。

多个报告器#

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

在以下示例中,spec报告器将输出到stdout,而dot报告器将输出到file.txt

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

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

run([options])#

  • options <Object>用于运行测试的配置选项。支持以下属性:
    • concurrency <数字> | <boolean>如果提供了一个数字,那么许多测试进程将并行运行,其中每个进程对应一个测试文件。如果是true,它将并行运行os.availableParallelism() - 1测试文件。如果是false,则一次只会运行一个测试文件。 默认值: false
    • files : <Array>包含要运行的文件列表的数组。 测试运行器执行模型中的默认匹配文件。
    • inspectPort <数字> | <函数>设置测试子进程的检查器端口。这可以是一个数字,也可以是一个不带参数并返回数字的函数。如果提供了 null 值,则每个进程都会获得自己的端口,该端口从主进程的process.debugPort递增。 默认值: undefined
    • setup <Function>接受TestsStream实例的函数,可用于在运行任何测试之前设置侦听器。 默认值: undefined
    • signal <AbortSignal>允许中止正在进行的测试执行。
    • testNamePatterns <字符串> | <正则表达式> | <Array>字符串、RegExp 或 RegExp 数组,可用于仅运行名称与提供的模式匹配的测试。测试名称模式被解释为 JavaScript 正则表达式。对于执行的每个测试,还会运行 任何相应的测试挂钩,例如 beforeEach()默认值: undefined
    • timeout <number>测试执行失败的毫秒数。如果未指定,子测试将从其父测试继承此值。 默认值: Infinity
    • watch <boolean>是否以监视模式运行。默认值: false
  • 返回:<测试流>
import { tap } from 'node:test/reporters';
import process from 'node:process';

run({ files: [path.resolve('./tests/test.js')] })
  .compose(tap)
  .pipe(process.stdout);const { tap } = require('node:test/reporters');

run({ files: [path.resolve('./tests/test.js')] })
  .compose(tap)
  .pipe(process.stdout);

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

  • name <string>测试的名称,在报告测试结果时显示。默认值fnname属性,如果fn 没有名称,则为 '<anonymous>'
  • options <Object>测试的配置选项。支持以下属性:
    • concurrency <数字> | <boolean>如果提供了一个数字,那么许多测试将在应用程序线程中并行运行。如果true,则所有计划的异步测试在线程内同时运行。如果是false,则一次仅运行一个测试。如果未指定,子测试将从其父测试继承此值。 默认值: false
    • only <boolean>如果为 true,并且测试上下文配置为运行 only测试,则将运行此测试。否则,测试将被跳过。 默认值: false
    • signal <AbortSignal>允许中止正在进行的测试。
    • skip <布尔值> | <string>如果为 true,则跳过测试。如果提供了字符串,则该字符串将作为跳过测试的原因显示在测试结果中。默认值: false
    • todo <布尔值> | <string>如果为 true,则测试标记为TODO。如果提供了字符串,则该字符串将显示在测试结果中,作为测试为TODO的原因。默认值: false
    • timeout <number>测试失败的毫秒数。如果未指定,子测试将从其父测试继承此值。 默认值: Infinity
  • fn <函数> | <AsyncFunction>正在测试的函数。该函数的第一个参数是一个TestContext对象。如果测试使用回调,则回调函数将作为第二个参数传递。默认值:无操作函数。
  • 返回:<Promise>一旦测试完成,就用undefined解决,或者如果测试在describe()内运行,则立即解决。

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

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

test()返回一个Promise,测试完成后即可解析。如果在describe()块内调用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选项使测试失败。但是,它不是取消测试的可靠机制,因为正在运行的测试可能会阻塞应用程序线程,从而阻止计划的取消。

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

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

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

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

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

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

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

  • name <string>套件的名称,在报告测试结果时显示。默认值fnname属性,如果fn 没有名称,则为 '<anonymous>'
  • options <对象>套件的配置选项。支持与test([name][, options][, fn])相同的选项。
  • fn <函数> | <AsyncFunction>套件下的函数声明所有子测试和子套件。该函数的第一个参数是一个SuiteContext对象。 默认值:无操作函数。
  • 返回:<Promise>立即履行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])#

test()的简写。

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

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

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

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

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

类:MockFunctionContext#

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

ctx.calls#

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

  • arguments <Array>传递给模拟函数的参数数组。
  • error <any>如果模拟函数抛出异常,则此属性包含抛出的值。默认值: undefined
  • result <any>模拟函数返回的值。
  • stack <Error>一个Error对象,其堆栈可用于确定模拟函数调用的调用点。
  • target <函数> | <undefined>如果模拟函数是构造函数,则此字段包含正在构造的类。否则这将是 undefined
  • this <any>模拟函数的this值。

ctx.callCount()#

  • 返回:<integer>该模拟已被调用的次数。

此函数返回此模拟已被调用的次数。此函数比检查ctx.calls.length更有效,因为ctx.calls 是一个 getter,用于创建内部调用跟踪数组的副本。

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 <函数> | <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 <函数> | <AsyncFunction>用于创建模拟的可选函数。 默认值:无操作函数。
  • implementation <函数> | <AsyncFunction>用作original的模拟实现的可选函数。这对于创建模拟来展示指定次数的调用的一种行为然后恢复original的行为非常有用。默认值:由original指定的函数。
  • options <Object>模拟函数的可选配置选项。支持以下属性:
    • times <integer>模拟将使用implementation行为的次数 。一旦模拟函数被调用times次,它将自动恢复original的行为。该值必须是大于零的整数。默认值: Infinity
  • 返回:<Proxy>模拟函数。模拟函数包含一个特殊的 mock属性,它是MockFunctionContext的实例,可用于检查和更改模拟函数的行为。

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

以下示例创建一个模拟函数,该函数在每次调用时将计数器加一。times选项用于修改模拟行为,以便前两次调用向计数器添加 2 而不是 1。

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 <字符串> | <symbol> object上要模拟的方法的标识符。如果object[methodName]不是函数,则会引发错误。
  • implementation <函数> | <AsyncFunction>用作object[methodName]模拟实现的可选函数。默认值:由object[methodName]指定的原始方法。
  • options <Object>模拟方法的可选配置选项。支持以下属性:
    • getter <boolean>如果true,则 object[methodName]被视为 getter。此选项不能与setter选项一起使用。默认值:假。
    • setter <boolean>如果true,则 object[methodName]被视为 setter。此选项不能与getter选项一起使用。默认值:假。
    • 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 <对象>
    • 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:dequeue'#

当测试出列时(在执行之前)发出。

事件:'test:diagnostic'#

调用context.diagnostic时发出。

事件:'test:enqueue'#

当测试排队执行时发出。

事件:'test:fail'#

测试失败时发出。

事件:'test:pass'#

测试通过时发出。

事件:'test:plan'#

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

事件:'test:start'#

当测试开始报告其自身及其子测试状态时发出。该事件保证按照测试定义的顺序发出。

事件:'test:stderr'#

当正在运行的测试写入stderr时发出。仅当传递--test标志时才会发出此事件。

事件:'test:stdout'#

当正在运行的测试写入stdout时发出。仅当传递--test标志时才会发出此事件。

事件:'test:watch:drained'#

当没有更多测试在监视模式下排队执行时发出。

类:TestContext#

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

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

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

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

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

  • fn <函数> | <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 <函数> | <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 <函数> | <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)#

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

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) => {
  // 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>可用于在测试已中止时中止测试子任务。
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) => {
  // 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()不会终止测试函数的执行。该函数不返回值。

test('top level test', (t) => {
  // This test is marked as `TODO`
  t.todo('this is a todo');
}); 

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

  • name <string>子测试的名称,在报告测试结果时显示。默认值fnname属性,如果 fn没有名称,则为 '<anonymous>'
  • options <Object>子测试的配置选项。支持以下属性:
    • concurrency <数字> | <布尔值> | <null>如果提供了一个数字,那么许多测试将在应用程序线程中并行运行。如果是true,它将并行运行所有子测试。如果是false,则一次只会运行一个测试。如果未指定,子测试将从其父测试继承此值。 默认值: null
    • only <boolean>如果为 true,并且测试上下文配置为运行 only测试,则将运行此测试。否则,测试将被跳过。 默认值: false
    • signal <AbortSignal>允许中止正在进行的测试。
    • skip <布尔值> | <string>如果为 true,则跳过测试。如果提供了字符串,则该字符串将作为跳过测试的原因显示在测试结果中。默认值: false
    • todo <布尔值> | <string>如果为 true,则测试标记为TODO。如果提供了字符串,则该字符串将显示在测试结果中,作为测试为TODO的原因。默认值: false
    • timeout <number>测试失败的毫秒数。如果未指定,子测试将从其父测试继承此值。 默认值: Infinity
  • fn <函数> | <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>可用于在测试已中止时中止测试子任务。

NodeJS中文文档为Read dev Docs平台提供托管,中文NodeJS文档均由英文版NodeJS文档翻译,版权属于nodejs.org