Skip to content

Test runner

node:test 模块有助于创建 JavaScript 测试。要想访问他,请执行以下操作:

js
const test = require('node:test')

此模块仅在 方案下可用 node: 以下情况将不起作用:

js
const test = require('test');

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

  1. 如果同步函数抛出异常则视为失败,否则视为通过。
  2. 返回一个函数,Promise如果拒绝则被视为失败 Promise,如果满足则被视为通过Promise。
  3. 接收回调函数的函数。如果回调函数接收任何真值作为其第一个参数,则测试被视为失败。如果将假值作为回调函数的第一个参数传递,则测试被视为通过。如果测试函数接收回调函数并返回Promise,则测试将失败。

下面的示例说明如何使用该模块编写测试 test

js
const test = require('node:test')
const assert = require('node:assert')

test('测试通过', (t) => {
  assert.strictEqual(1, 1);
});

test('测试失败', (t) => {
  assert.strictEqual(1, 2);
});

// 使用 async 也可以
test('测试通过', async (t) => {
  assert.strictEqual(1, 1);
});

test('测试失败', async (t) => {
  assert.strictEqual(1, 2);
});

test('使用 Promises 测试失败', (t) => {
  // Promise 也可以直接使用.
  return new Promise((resolve, reject) => {
    setImmediate(() => {
      reject(new Error('这将导致测试失败'));
    });
  });
});

test('回调通过测试', (t, done) => {
  // done()是回调函数。当 setImmediate() 运行时,它会调用
  // done() 不带参数的
  setImmediate(done);
});

test('回调失败测试', (t, done) => {
  setImmediate(() => {
    done(new Error('回调失败'));
  });
});

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

子测试

测试上下文的 test() 方法允许创建子测试。它允许您以分层方式构建测试,您可以在更大的测试中创建嵌套测试。此方法的行为与顶级 test() 函数相同。以下示例演示了如何创建具有两个子测试的顶级测试。

js
test('顶级测试', async (t) => {
  await t.test('子测试 1', (t) => {
    assert.strictEqual(1, 1);
  });

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

注意: 每次子测试执行之间都会触发 beforeEachafterEach 钩子。

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

跳过测试

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

js
// 使用了跳过选项,但没有提供任何消息
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('跳过');
});

TODO 测试

todo 通过将选项传递给测试或调用测试上下文的方法,可以将单个测试标记为不稳定或不完整 todo(),如以下示例所示。这些测试表示待处理的实现或需要修复的错误。TODO 测试会执行,但不会被视为测试失败,因此不会影响流程退出代码。如果测试同时标记为 TODO 和跳过,则 TODO 选项将被忽略。

js
// 使用了 todo 选项,但没有提供任何消息。
test('todo option', { todo: true }, (t) => {
  // 该代码被执行,但不被视为失败。
  throw new Error('this does not fail the test');
});

test('todo 测试', { todo: 'todo 信息' }, (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() 套件和测试也可以使用和函数来编写 it()describe() 是的别名 suite(),并且 it() 是的别名 test()

describe()it() 从模块导入 node:test

js
const { describe, it } = require('node:test');
js
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);
    });
  });
});

按名称过滤测试

命令 --test-name-pattern 行选项可用于仅运行名称与提供的模式匹配的测试,而选项 --test-skip-pattern 可用于跳过名称与提供的模式匹配的测试。测试名称模式被解释为 JavaScript 正则表达式。--test-name-pattern--test-skip-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" 等)。

js
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 因为该模式不区分大小写。

从命令行运行测试

可以通过传递以下标志从命令行调用 Node.js 测试运行器 --test

bash
node --test

默认情况下,Node.js 将运行所有与这些模式匹配的文件:

  • **/*.test.?(c|m)js
  • **/*-test.?(c|m)js
  • **/*_test.?(c|m)js
  • **/test-*.?(c|m)js
  • **/test.?(c|m)js
  • /test//*.?(c|m)js
bash
node --test "**/*.test.js" "**/*.spec.js"

收集代码覆盖率

注意 node 版本

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

钩子

before([fn][, options])

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

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

js
describe('tests', async () => {
  before(() => console.log('about to run some test'));
  it('is a subtest', () => {
    assert.ok('some relevant assertion here');
  });
});

after([fn][, options])

此函数创建一个在执行套件后运行的钩子。

与 bafor 相同,只是执行的时机不同

beforeEach([fn][, options])

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

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

js
describe('tests', async () => {
  beforeEach(() => console.log('about to run a test'));
  it('is a subtest', () => {
    assert.ok('some relevant assertion here');
  });
});

Released under the MIT License.