Spec-Zone.ru › Node.js

Средство запуска тестов

Исходный код: lib/test.jsДобавлено в: v18.0.0, v16.17.0
История
Версия Изменения
v20.0.0

Средство запуска тестов теперь стабильно.

Стабильность: 2 — Стабильно

Модуль node:test упрощает создание тестов JavaScript. Для доступа к нему:

Модули JavaScript
import test from 'node:test';
CommonJS
const test = require('node:test');

Этот модуль доступен только при использовании схемы node:.

Тесты, созданные с помощью модуля 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 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'));
  });
});
jscopy

Если какие-либо тесты завершатся неудачно, код завершения процесса будет равен 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);
  });
});
jscopy

Примечание: хуки beforeEach и afterEach вызываются между выполнениями каждого подтеста.

В этом примере await используется для проверки завершения обоих подтестов. Это необходимо, поскольку тесты не дожидаются завершения своих подтестов, в отличие от тестов, созданных внутри наборов тестов. Все незавершенные подтесты отменяются и считаются неудачными, когда завершается родительский тест. Любой неудачный подтест приводит к неудачному завершению родительского теста.

Повторный запуск неудачных тестов

Средство запуска тестов поддерживает сохранение состояния запуска в файл, что позволяет повторно запускать неудачные тесты без повторного запуска всего набора тестов. Используйте параметр командной строки --test-rerun-failures, чтобы указать путь к файлу, в котором будет сохранено состояние запуска. Если файл состояния не существует, средство запуска тестов создаст его. Файл состояния имеет формат JSON и содержит массив попыток запуска. Каждая попытка запуска — это объект, сопоставляющий успешно пройденные тесты с номером попытки, в которой они были пройдены. Ключом, идентифицирующим тест в этой карте, является путь к файлу теста с указанием строки и столбца, где определен тест. Если тест, определенный в конкретном месте, запускается несколько раз, например внутри функции или цикла, к ключу добавляется счетчик, чтобы различать запуски теста. Обратите внимание: изменение порядка выполнения тестов или расположения теста может привести к тому, что средство запуска тестов сочтет тесты пройденными в предыдущей попытке. Поэтому --test-rerun-failures следует использовать, когда тесты выполняются в детерминированном порядке.

Пример файла состояния:

[
  {
    "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" }
  }
]
jsoncopy

В этом примере показаны две попытки запуска и два теста, определенных в test.js: первый тест успешно пройден при первой попытке, а второй — при второй.

Если используется параметр --test-rerun-failures, средство запуска тестов будет запускать только тесты, которые еще не были пройдены.

node --test-rerun-failures /path/to/state/file
bashcopy

Псевдонимы describe() и it()

Наборы тестов и тесты также можно записывать с помощью функций describe() и it(). describe() — это псевдоним для suite(), а it() — псевдоним для 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);
    });
  });
});
jscopy

describe() и it() импортируются из модуля node:test.

Модули JavaScript
import { describe, it } from 'node:test';
CommonJS
const { describe, it } = require('node:test');

Пропуск тестов

Отдельные тесты можно пропустить, передав тесту параметр 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');
});
jscopy

Тесты TODO

Отдельные тесты можно пометить как нестабильные или незавершенные, передав тесту параметр todo либо вызвав метод todo() контекста теста, как показано в следующем примере. Такие тесты указывают на незавершенную реализацию или ошибку, которую необходимо исправить. Тесты TODO выполняются, но не считаются неудачными и поэтому не влияют на код завершения процесса. Если тест помечен одновременно как TODO и пропущенный, параметр TODO игнорируется.

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

Ожидание неудачного завершения тестов

Добавлено в: v25.5.0, v24.14.0

Это меняет на противоположные результаты успешного и неудачного выполнения для определенного теста или набора тестов: помеченный тест должен выбросить исключение, чтобы считаться успешно пройденным, а помеченный тест, не выбросивший исключение, считается неудачным.

В каждом из следующих примеров doTheThing() не возвращает true, но, поскольку тесты помечены expectFailure, они считаются успешно пройденными.

it.expectFailure('should do the thing', () => {
  assert.strictEqual(doTheThing(), true);
});

it('should do the thing', { expectFailure: true }, () => {
  assert.strictEqual(doTheThing(), true);
});

it('should do the thing', { expectFailure: 'feature not implemented' }, () => {
  assert.strictEqual(doTheThing(), true);
});
jscopy

Если значение expectFailure — это <RegExp> | <Function> | <Object> | <Error>, тесты будут успешно пройдены только в том случае, если выброшенное значение соответствует заданному. О том, как обрабатывается каждый тип значения, см. в разделе assert.throws.

Каждый из следующих тестов завершается неудачно, несмотря на то что он помечен expectFailure, поскольку сбой не соответствует конкретному ожидаемому сбою.

it('fails because regex does not match', {
  expectFailure: /expected message/,
}, () => {
  throw new Error('different message');
});

it('fails because object matcher does not match', {
  expectFailure: { code: 'ERR_EXPECTED' },
}, () => {
  const err = new Error('boom');
  err.code = 'ERR_ACTUAL';
  throw err;
});
jscopy

Чтобы указать причину и конкретную ошибку для expectFailure, используйте { label, match }.

it('should fail with specific error and reason', {
  expectFailure: {
    label: 'reason for failure',
    match: /error message/,
  },
}, () => {
  assert.strictEqual(doTheThing(), true);
});
jscopy

skip и/или todo несовместимы с expectFailure, а при одновременном указании skip или todo имеет приоритет (skip имеет приоритет над обоими параметрами, а todo — над expectFailure).

Эти тесты будут пропущены (и не будут запущены):

it.expectFailure('should do the thing', { skip: true }, () => {
  assert.strictEqual(doTheThing(), true);
});

it.skip('should do the thing', { expectFailure: true }, () => {
  assert.strictEqual(doTheThing(), true);
});
jscopy

Эти тесты будут помечены как «todo» (ошибки будут подавлены):

it.expectFailure('should do the thing', { todo: true }, () => {
  assert.strictEqual(doTheThing(), true);
});

it.todo('should do the thing', { expectFailure: true }, () => {
  assert.strictEqual(doTheThing(), true);
});
jscopy

Только тесты only

Если Node.js запущен с параметром командной строки --test-only или изоляция тестов отключена, можно пропустить все тесты, кроме выбранного подмножества, передав параметр only тестам, которые нужно запустить. Если для теста задан параметр only, запускаются также все его подтесты. Если для набора тестов задан параметр only, запускаются все содержащиеся в нем тесты, если только у него нет потомков с заданным параметром only; в этом случае запускаются только эти тесты.

При использовании подтестов внутри test()/it() для запуска только выбранного подмножества тестов необходимо пометить параметром only все тесты-предки.

Для реализации такого же поведения на уровне подтестов можно использовать метод runOnly() контекста теста. Не выполненные тесты не отображаются в выводе средства запуска тестов.

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

Фильтрация тестов по имени

Параметр командной строки --test-name-pattern позволяет запускать только тесты, имена которых соответствуют заданному шаблону, а параметр --test-skip-pattern позволяет пропускать тесты, имена которых соответствуют заданному шаблону. Шаблоны имен тестов интерпретируются как регулярные выражения JavaScript. Параметры --test-name-pattern и --test-skip-pattern можно указывать несколько раз для запуска вложенных тестов. Для каждого выполненного теста также запускаются соответствующие хуки, например beforeEach(). Не выполненные тесты не отображаются в выводе средства запуска тестов.

Для следующего файла теста запуск Node.js с параметром --test-name-pattern="test [1-3]" приведет к тому, что средство запуска тестов выполнит test 1, test 2 и test 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');
});
jscopy

Шаблоны имен тестов также можно задавать с помощью литералов регулярных выражений. Это позволяет использовать флаги регулярных выражений. В предыдущем примере запуск Node.js с --test-name-pattern="/test [4-5]/i" (или --test-skip-pattern="/test [4-5]/i") будет соответствовать Test 4 и Test 5, поскольку шаблон не учитывает регистр.

Чтобы сопоставить шаблон с одним тестом, можно добавить перед ним имена всех тестов-предков, разделенные пробелами, чтобы обеспечить уникальность. Например, для следующего файла теста:

describe('test 1', (t) => {
  it('some test');
});

describe('test 2', (t) => {
  it('some test');
});
jscopy

Запуск Node.js с --test-name-pattern="test 1 some test" будет соответствовать только some test в test 1.

Шаблоны имен тестов не меняют набор файлов, выполняемых средством запуска тестов.

Если заданы и --test-name-pattern, и --test-skip-pattern, для выполнения тесты должны удовлетворять обоим условиям.

Теги тестов

Добавлено в: v26.2.0

Стабильность: 1.0 — Ранняя стадия разработки

Теги присваивают тестам и наборам тестов произвольные строковые метки. Флаг CLI --experimental-test-tag-filter (или параметр testTagFilters в run()) выбирает тесты, набор тегов которых содержит каждое из указанных значений фильтра.

Теги — это альтернатива кодированию метаданных в именах тестов. Они полезны для сквозной классификации по таким признакам, как подсистема, категория скорости, нестабильность или среда, для которых шаблон имени был бы ненадежным.

Создание тестов с тегами

Передайте массив tags любой из функций test(), it(), suite() или describe(). Теги наследуются от набора тестов дочерними тестами путем объединения: тест внутри набора с тегом ['db'], объявляющий собственный tags: ['integration'], фактически получает оба тега.

Модули JavaScript
import { describe, it } from 'node:test';

describe('database', { tags: ['db'] }, () => {
  it('reads a row');                                            // tags: ['db']
  it('writes a row', { tags: ['integration'] });                // tags: ['db', 'integration']
  it('reconnects after disconnect', { tags: ['flaky'] });       // tags: ['db', 'flaky']
});
CommonJS
const { describe, it } = require('node:test');

describe('database', { tags: ['db'] }, () => {
  it('reads a row');                                            // tags: ['db']
  it('writes a row', { tags: ['integration'] });                // tags: ['db', 'integration']
  it('reconnects after disconnect', { tags: ['flaky'] });       // tags: ['db', 'flaky']
});

Значения тегов должны быть непустыми строками. При сопоставлении тегов регистр не учитывается; каноническая форма — нижний регистр. Дубликаты в одном массиве tags объединяются по форме в нижнем регистре с сохранением порядка первого объявления.

Хуки (before, after, beforeEach, afterEach) не объявляют собственные теги. Они выполняются как часть содержащего их набора тестов, которому принадлежат теги этого набора.

Фильтрация по тегам

Каждое значение --experimental-test-tag-filter — это буквальное имя тега. Тест запускается только в том случае, если его набор тегов содержит это имя. Флаг можно указывать несколько раз; для запуска тесты должны соответствовать каждому фильтру. То же относится к массиву testTagFilters в run(). Фильтры не учитывают регистр и применяются совместно с фильтрацией по --test-name-pattern, --test-skip-pattern и .only.

Тесты без тегов исключаются при любом непустом фильтре, поскольку фильтр требует наличия тега.

Чтение тегов из теста

Объект TestContext предоставляет теги теста в виде замороженного массива через context.tags, поэтому тесты могут проверять собственные метаданные.

Ошибки

Значение тега, нарушающее приведенные выше правила проверки, приводит к выбросу ERR_INVALID_ARG_VALUE в месте регистрации до запуска любого теста. Значение tags, не являющееся массивом, приводит к выбросу ERR_INVALID_ARG_TYPE.

Посторонняя асинхронная активность

После завершения выполнения тестовой функции результаты сообщаются как можно скорее, при этом сохраняется порядок тестов. Однако тестовая функция может создать асинхронную активность, которая продолжится после завершения самого теста. Средство запуска тестов обрабатывает такую активность, но не откладывает вывод результатов тестов, чтобы дождаться ее завершения.

В следующем примере тест завершается, когда остаются невыполненными две операции setImmediate(). Первая setImmediate() пытается создать новый подтест. Поскольку родительский тест уже завершился и его результаты выведены, новый подтест сразу помечается как неудачный, а позднее передается в <TestsStream>.

Вторая setImmediate() создает событие uncaughtException. События uncaughtException и unhandledRejection, происходящие от завершенного теста, помечаются как неудачные модулем 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.
});
jscopy

Режим наблюдения

Добавлено в: v19.2.0, v18.13.0

Стабильность: 1 — Экспериментальный

Средство запуска тестов Node.js поддерживает режим наблюдения, который включается флагом --watch:

node --test --watch
bashcopy

В режиме наблюдения средство запуска тестов отслеживает изменения в файлах тестов и их зависимостях. При обнаружении изменения оно повторно запускает затронутые тесты. Средство запуска тестов продолжает работу, пока процесс не будет завершен.

Глобальная настройка и очистка

Добавлено в: v24.0.0

Стабильность: 1.0 — Ранняя стадия разработки

Средство запуска тестов позволяет указать модуль, который будет выполнен до запуска всех тестов и который можно использовать для настройки глобального состояния или тестовых фикстур. Это удобно для подготовки ресурсов или настройки общего состояния, необходимого нескольким тестам.

Этот модуль может экспортировать любое из следующего:

  • Функцию globalSetup, выполняемую один раз до начала всех тестов
  • Функцию globalTeardown, выполняемую один раз после завершения всех тестов

Модуль указывается с помощью флага --test-global-setup при запуске тестов из командной строки.

CommonJS
// 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 };
Модули JavaScript
// 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.
}

Если функция глобальной настройки выбрасывает ошибку, тесты не запускаются, а процесс завершается с ненулевым кодом завершения. В этом случае функция глобальной очистки не вызывается.

Запуск тестов из командной строки

Средство запуска тестов Node.js можно вызвать из командной строки, передав флаг --test:

node --test
bashcopy

По умолчанию Node.js запускает все файлы, соответствующие следующим шаблонам:

  • **/*.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, дополнительно сопоставляются следующие шаблоны:

  • **/*.test.{cts,mts,ts}
  • **/*-test.{cts,mts,ts}
  • **/*_test.{cts,mts,ts}
  • **/test-*.{cts,mts,ts}
  • **/test.{cts,mts,ts}
  • **/test/**/*.{cts,mts,ts}

Также можно указать один или несколько шаблонов glob в качестве последних аргументов команды Node.js, как показано ниже. Шаблоны glob работают так же, как glob(7). Шаблоны glob следует заключать в двойные кавычки в командной строке, чтобы предотвратить раскрытие оболочкой, которое может снизить переносимость между системами.

node --test "**/*.test.js" "**/*.spec.js"
bashcopy

Случайный порядок выполнения тестов

Добавлено в: v26.1.0

Стабильность: 1.0 — Ранняя стадия разработки

Средство запуска тестов может выполнять тесты в случайном порядке, чтобы помочь обнаруживать зависимость тестов от порядка выполнения. В этом режиме средство запуска меняет порядок как обнаруженных файлов тестов, так и поставленных в очередь тестов внутри каждого файла. Для включения этого режима используйте --test-randomize.

node --test --test-randomize
bashcopy

При включенной рандомизации средство запуска тестов выводит использованное для запуска начальное значение в виде диагностического сообщения:

Randomized test order seed: 12345
textcopy

Используйте --test-random-seed=<number>, чтобы детерминированно повторить тот же случайный порядок. Указание --test-random-seed также включает рандомизацию, поэтому --test-randomize необязателен, если задано начальное значение:

node --test --test-random-seed=12345
bashcopy

В большинстве файлов тестов рандомизация работает автоматически. Важное исключение — последовательное ожидание подтестов. В этом случае каждый подтест начинается только после завершения предыдущего, поэтому средство запуска сохраняет порядок объявления, а не меняет его случайным образом.

Пример: этот код выполняется последовательно и не рандомизируется.

Модули JavaScript
import test from 'node:test';

test('math', async (t) => {
  for (const name of ['adds', 'subtracts', 'multiplies']) {
    // Sequentially awaiting each subtest preserves declaration order.
    await t.test(name, async () => {});
  }
});
CommonJS
const test = require('node:test');

test('math', async (t) => {
  for (const name of ['adds', 'subtracts', 'multiplies']) {
    // Sequentially awaiting each subtest preserves declaration order.
    await t.test(name, async () => {});
  }
});

Использование API в стиле наборов тестов, например describe()/it() или suite()/test(), по-прежнему допускает рандомизацию, поскольку соседние тесты ставятся в очередь одновременно.

Пример: этот код по-прежнему допускает рандомизацию.

Модули JavaScript
import { describe, it } from 'node:test';

describe('math', () => {
  it('adds', () => {});
  it('subtracts', () => {});
  it('multiplies', () => {});
});
CommonJS
const { describe, it } = require('node:test');

describe('math', () => {
  it('adds', () => {});
  it('subtracts', () => {});
  it('multiplies', () => {});
});

--test-randomize и --test-random-seed не поддерживаются в режиме --watch.

Совпавшие файлы выполняются как файлы тестов. Дополнительные сведения о выполнении файлов тестов приведены в разделе модель выполнения средства запуска тестов.

Модель выполнения средства запуска тестов

Если включена изоляция тестов на уровне процесса, каждый совпавший файл теста выполняется в отдельном дочернем процессе. Максимальное количество дочерних процессов, работающих одновременно, контролируется флагом --test-concurrency. Если дочерний процесс завершается с кодом 0, тест считается успешно пройденным. В противном случае тест считается неудачным. Файлы тестов должны выполняться Node.js, но не обязаны использовать внутри себя модуль node:test.

Каждый файл теста выполняется как обычный скрипт. То есть, если сам файл теста использует node:test для определения тестов, все эти тесты будут выполнены в одном потоке приложения независимо от значения параметра concurrency функции test().

Если изоляция тестов на уровне процесса отключена, каждый совпавший файл теста импортируется в процесс средства запуска тестов. После загрузки всех файлов тестов тесты верхнего уровня выполняются с уровнем параллелизма, равным единице. Поскольку все файлы тестов выполняются в одном контексте, тесты могут взаимодействовать друг с другом способами, невозможными при включенной изоляции. Например, состояние, от которого зависит тест, может быть изменено тестом из другого файла.

Наследование параметров дочерними процессами

При запуске тестов в режиме изоляции процессов (по умолчанию) дочерние процессы наследуют параметры Node.js родительского процесса, включая параметры, указанные в файлах конфигурации. Однако некоторые флаги отфильтровываются для корректной работы средства запуска тестов:

  • --test — отключается во избежание рекурсивного выполнения тестов
  • --experimental-test-coverage — управляется средством запуска тестов
  • --experimental-test-tag-filter — значения фильтров проверяются родительским процессом и повторно передаются дочерним процессам
  • --watch — режим наблюдения обрабатывается на уровне родительского процесса
  • --experimental-default-config-file — загрузкой файла конфигурации управляет родительский процесс
  • --test-reporter — формированием отчетов управляет родительский процесс
  • --test-reporter-destination — места назначения вывода контролируются родительским процессом
  • --experimental-config-file — путями к файлам конфигурации управляет родительский процесс
  • --test-randomize — рандомизацией управляет родительский процесс, который передает ее параметры дочерним процессам
  • --test-random-seed — начальным значением для рандомизации управляет родительский процесс, который передает его дочерним процессам

Все остальные параметры Node.js из аргументов командной строки, переменных окружения и файлов конфигурации наследуются дочерними процессами.

Сбор данных о покрытии кода

Стабильность: 1 — Экспериментальный

Если Node.js запущен с флагом командной строки --experimental-test-coverage, собираются данные о покрытии кода, а статистика выводится после завершения всех тестов. Если для указания каталога покрытия кода используется переменная окружения NODE_V8_COVERAGE, созданные файлы покрытия V8 записываются в этот каталог. По умолчанию основные модули Node.js и файлы в каталогах node_modules/ не включаются в отчет о покрытии. Однако их можно явно включить с помощью флага --test-coverage-include. По умолчанию все совпавшие файлы тестов исключаются из отчета о покрытии. Переопределить исключения можно с помощью флага --test-coverage-exclude. Если сбор данных о покрытии включен, отчет передается всем средствам формирования отчетов о тестах через событие '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 */
jscopy

Сбор данных о покрытии также можно отключить для заданного количества строк. После указанного количества строк он будет автоматически включен снова. Если количество строк явно не указано, игнорируется одна строка.

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

Средства формирования отчетов о покрытии

Средства формирования отчетов tap и spec выводят сводку статистики покрытия. Также доступно средство формирования отчета lcov, которое создает файл lcov для подробного анализа покрытия.

node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=lcov.info
bashcopy
  • Это средство формирования отчетов не сообщает результаты тестов.
  • В идеале это средство следует использовать вместе с другим средством формирования отчетов.

Имитация

Модуль node:test поддерживает имитацию при тестировании с помощью объекта верхнего уровня mock. В следующем примере создается шпион для функции, складывающей два числа. Затем с помощью шпиона проверяется, что функция была вызвана ожидаемым образом.

Модули JavaScript
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();
});
CommonJS
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 каждого теста. В следующем примере создается шпион для метода объекта с использованием API, предоставляемого объектом TestContext. Преимущество имитации через контекст теста заключается в том, что средство запуска тестов автоматически восстанавливает всю имитированную функциональность после завершения теста.

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

Таймеры

Имитация таймеров — часто используемый в тестировании программного обеспечения способ моделирования и контроля поведения таймеров, таких как setInterval и setTimeout, без фактического ожидания заданных интервалов времени.

Полный список методов и возможностей см. в описании класса MockTimers.

Это позволяет разработчикам писать более надежные и предсказуемые тесты для функциональности, зависящей от времени.

В примере ниже показано, как имитировать setTimeout. С помощью .enable({ apis: ['setTimeout'] }); будут имитироваться функции setTimeout в модулях node:timers и node:timers/promises, а также в глобальном контексте Node.js.

Примечание: Деструктуризация функций, например import { setTimeout } from 'node:timers', в настоящее время не поддерживается этим API.

Модули JavaScript
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();
});
CommonJS
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();
});

Та же функциональность имитации доступна в свойстве mock объекта TestContext каждого теста. Преимущество имитации через контекст теста заключается в том, что средство запуска тестов автоматически восстанавливает все имитированные таймеры после завершения теста.

Модули JavaScript
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);
});
CommonJS
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);
});

Даты

API имитации таймеров также позволяет имитировать объект Date. Эта возможность полезна для тестирования функциональности, зависящей от времени, или моделирования внутренних календарных функций, таких как Date.now().

Реализация имитации дат также является частью класса MockTimers. Полный список методов и возможностей см. в его описании.

Примечание: При совместной имитации даты и таймеры связаны между собой. Это означает, что при имитации обоих объектов — Date и setTimeout — продвижение времени также сдвинет имитированную дату, поскольку они моделируют единые внутренние часы.

В примере ниже показано, как имитировать объект Date и получить текущее значение Date.now().

Модули JavaScript
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);
});
CommonJS
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. Это 1 января 1970 года, 00:00:00 UTC. Начальную дату можно задать, передав свойство now методу .enable(). Это значение будет использоваться как начальная дата имитированного объекта Date. Оно может быть положительным целым числом или другим объектом Date.

Модули JavaScript
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);
});
CommonJS
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() позволяет вручную переместить имитированную дату на другое время. Этот метод принимает только положительное целое число.

Примечание: Этот метод не выполнит имитированные таймеры, время которых уже прошло относительно нового времени.

В примере ниже задается новое время для имитированной даты.

Модули JavaScript
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);
});
CommonJS
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);
});

Таймеры, запланированные на прошедшее время, не будут запущены при вызове setTime(). Чтобы выполнить эти таймеры, можно использовать метод .tick() для продвижения времени вперед от нового значения.

Модули JavaScript
import assert from 'node:assert';
import { test } from 'node:test';

test('setTime does not execute timers', (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 still not executed
  assert.strictEqual(fn.mock.callCount(), 0);
  // Advance in time to execute the timer
  context.mock.timers.tick(0);
  assert.strictEqual(fn.mock.callCount(), 1);
  assert.strictEqual(Date.now(), 1200);
});
CommonJS
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() выполнит все таймеры, находящиеся в очереди. При этом имитированная дата также сдвинется на время выполнения последнего таймера, как если бы время действительно прошло.

Модули JavaScript
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);
});
CommonJS
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);
});

Тестирование снимков

Добавлено в: v22.3.0
История
Версия Изменения
v23.4.0

Тестирование снимков больше не является экспериментальным.

Тесты снимков позволяют сериализовать произвольные значения в строки и сравнивать их с набором известных корректных значений. Эти известные корректные значения называются снимками и хранятся в файле снимков. Файлами снимков управляет средство запуска тестов, но они разработаны так, чтобы оставаться удобочитаемыми для упрощения отладки. Рекомендуется добавлять файлы снимков в систему контроля версий вместе с файлами тестов.

Файлы снимков создаются при запуске Node.js с флагом командной строки --test-update-snapshots. Для каждого файла тестов создается отдельный файл снимков. По умолчанию файл снимков имеет то же имя, что и файл тестов, но с расширением файла .snapshot. Это поведение можно настроить с помощью функции snapshot.setResolveSnapshotPath(). Каждое утверждение для снимка соответствует экспорту в файле снимков.

Пример теста снимков приведен ниже. При первом выполнении этот тест завершится ошибкой, поскольку соответствующий файл снимков не существует.

// test.js
suite('suite of snapshot tests', () => {
  test('snapshot test', (t) => {
    t.assert.snapshot({ value1: 1, value2: 2 });
    t.assert.snapshot(5);
  });
});
jscopy

Создайте файл снимков, запустив файл тестов с помощью --test-update-snapshots. Тест должен пройти, а в том же каталоге, что и файл тестов, будет создан файл с именем test.js.snapshot. Ниже показано содержимое файла снимков. Каждый снимок обозначается полным именем теста и счетчиком, который позволяет различать снимки в рамках одного теста.

exports[`suite of snapshot tests > snapshot test 1`] = `
{
  "value1": 1,
  "value2": 2
}
`;

exports[`suite of snapshot tests > snapshot test 2`] = `
5
`;
jscopy

После создания файла снимков запустите тесты еще раз без флага --test-update-snapshots. Теперь тесты должны пройти.

Средства вывода результатов тестов

Добавлено в: v19.6.0, v18.15.0
История
Версия Изменения
v23.0.0

Средство вывода результатов по умолчанию для stdout без TTY изменено с tap на spec, чтобы соответствовать stdout с TTY.

v19.9.0, v18.17.0

Средства вывода результатов теперь доступны в node:test/reporters.

Модуль node:test поддерживает передачу флагов --test-reporter, чтобы средство запуска тестов использовало определенное средство вывода результатов.

Поддерживаются следующие встроенные средства вывода результатов:

  • spec Средство вывода результатов spec выводит результаты тестов в удобочитаемом формате. Это средство используется по умолчанию.

  • tap Средство вывода результатов tap выводит результаты тестов в формате TAP.

  • dot Средство вывода результатов dot выводит результаты тестов в компактном формате: каждый пройденный тест обозначается ., а каждый не пройденный — X.

  • junit Средство вывода результатов junit выводит результаты тестов в формате jUnit XML

  • lcov Средство вывода результатов lcov выводит сведения о покрытии тестами при использовании с флагом --experimental-test-coverage.

Точный формат вывода этих средств может меняться между версиями Node.js, поэтому на него не следует полагаться программно. Если требуется программный доступ к результатам работы средства запуска тестов, используйте события, генерируемые <TestsStream>.

Средства вывода результатов доступны через модуль node:test/reporters:

Модули JavaScript
import { tap, spec, dot, junit, lcov } from 'node:test/reporters';
CommonJS
const { tap, spec, dot, junit, lcov } = require('node:test/reporters');

Пользовательские средства вывода результатов

--test-reporter можно использовать, чтобы указать путь к пользовательскому средству вывода результатов. Пользовательское средство вывода результатов — это модуль, экспортирующий значение, принимаемое stream.compose. Средства вывода результатов должны преобразовывать события, генерируемые <TestsStream>

Пример пользовательского средства вывода результатов с использованием <stream.Transform>:

Модули JavaScript
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;
CommonJS
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;

Пример пользовательского средства вывода результатов с использованием функции-генератора:

Модули JavaScript
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;
      }
    }
  }
}
CommonJS
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, должно быть строкой, аналогичной той, которая используется в import() в коде JavaScript, или значением, передаваемым для --import.

Несколько средств вывода результатов

Флаг --test-reporter можно указывать несколько раз, чтобы выводить результаты тестов в нескольких форматах. В этом случае необходимо указать назначение для каждого средства вывода результатов с помощью --test-reporter-destination. Назначением может быть stdout, stderr или путь к файлу. Средства вывода результатов и назначения сопоставляются в соответствии с порядком их указания.

В следующем примере средство вывода результатов spec будет выводить данные в stdout, а средство dot — в file.txt:

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

Если указано только одно средство вывода результатов, назначением по умолчанию будет stdout, если явно не задано другое.

run([options])

Добавлено в: v18.9.0, v16.19.0
История
Версия Изменения
v26.2.0

Добавлен параметр testTagFilters.

v25.6.0, v24.14.0

Добавлен параметр env.

v24.7.0

Добавлен параметр rerunFailuresFilePath.

v23.0.0

Добавлен параметр cwd.

v23.0.0, v22.10.0

Добавлены параметры покрытия.

v22.8.0

Добавлен параметр isolation.

v22.6.0

Добавлен параметр globPatterns.

v22.0.0, v20.14.0

Добавлен параметр forceExit.

v20.1.0, v18.17.0

Добавлен параметр testNamePatterns.

  • options <Object> Параметры конфигурации для запуска тестов. Поддерживаются следующие свойства:
    • concurrency <number> | <boolean> Если указано число, параллельно запустится соответствующее количество процессов тестирования; каждый процесс соответствует одному файлу тестов. Если указано true, параллельно запустятся os.availableParallelism() - 1 файлов тестов. Если указано false, за один раз будет выполняться только один файл тестов. По умолчанию: false.
    • cwd <string> Задает текущий рабочий каталог, используемый средством запуска тестов. Служит базовым путем для разрешения путей к файлам, как если бы тесты запускались из командной строки в этом каталоге. По умолчанию: process.cwd().
    • files <Array> Массив со списком запускаемых файлов. По умолчанию: То же, что и при запуске тестов из командной строки.
    • forceExit <boolean> Настраивает средство запуска тестов так, чтобы оно завершало процесс после выполнения всех известных тестов, даже если в противном случае цикл событий оставался бы активным. По умолчанию: false.
    • globPatterns <Array> Массив с шаблонами glob для поиска файлов тестов. Этот параметр нельзя использовать вместе с files. По умолчанию: То же, что и при запуске тестов из командной строки.
    • inspectPort <number> | <Function> Задает порт инспектора дочернего процесса тестирования. Это может быть число или функция без аргументов, возвращающая число. Если указано значение nullish, каждому процессу назначается собственный порт, начиная с process.debugPort основного процесса и увеличивая его. Этот параметр игнорируется, если параметр isolation установлен в 'none', поскольку дочерние процессы не создаются. По умолчанию: undefined.
    • isolation <string> Настраивает тип изоляции тестов. Если установлено значение 'process', каждый файл тестов запускается в отдельном дочернем процессе. Если установлено значение 'none', все файлы тестов запускаются в текущем процессе. По умолчанию: 'process'.
    • only <boolean> Если значение истинное, контекст тестирования будет запускать только тесты, для которых задан параметр only
    • setup <Function> Функция, принимающая экземпляр TestsStream и позволяющая настроить обработчики событий до запуска тестов. По умолчанию: undefined.
    • execArgv <Array> Массив флагов CLI, передаваемых исполняемому файлу node при создании дочерних процессов. Этот параметр не действует, если isolation имеет значение 'none'. По умолчанию: []
    • argv <Array> Массив флагов CLI, передаваемых каждому файлу тестов при создании дочерних процессов. Этот параметр не действует, если isolation имеет значение 'none'. По умолчанию: [].
    • signal <AbortSignal> Позволяет прервать выполняющийся тест.
    • testNamePatterns <string> | <RegExp> | <Array> Строка, регулярное выражение или массив регулярных выражений, позволяющие запускать только тесты, имена которых соответствуют заданному шаблону. Шаблоны имен тестов интерпретируются как регулярные выражения JavaScript. Для каждого выполняемого теста запускаются также все связанные с ним обработчики, например beforeEach(). По умолчанию: undefined.
    • testSkipPatterns <string> | <RegExp> | <Array> Строка, регулярное выражение или массив регулярных выражений, позволяющие исключить запуск тестов, имена которых соответствуют заданному шаблону. Шаблоны имен тестов интерпретируются как регулярные выражения JavaScript. Для каждого выполняемого теста запускаются также все связанные с ним обработчики, например beforeEach(). По умолчанию: undefined.
    • testTagFilters <string> | <string[]> Имя тега или массив имен тегов, используемых для фильтрации тестов по указанным тегам. Для запуска тесты должны содержать все перечисленные теги. Эквивалентно передаче --experimental-test-tag-filter в командной строке. См. раздел Теги тестов. По умолчанию: undefined.
    • timeout <number> Время в миллисекундах, после которого выполнение теста завершится ошибкой. Если значение не задано, вложенные тесты наследуют его от родительского теста. По умолчанию: Infinity.
    • watch <boolean> Определяет, следует ли запускать тесты в режиме наблюдения. По умолчанию: false.
    • shard <Object> Запуск тестов в определенном сегменте. По умолчанию: undefined.
      • index <number> — положительное целое число от 1 до <total>, задающее индекс запускаемого сегмента. Этот параметр обязателен.
      • total <number> — положительное целое число, задающее общее количество сегментов, на которые следует разделить файлы тестов. Этот параметр обязателен.
    • randomize <boolean> Случайным образом меняет порядок выполнения файлов тестов и тестов в очереди. Этот параметр не поддерживается вместе с watch: true. По умолчанию: false.
    • randomSeed <number> Начальное значение для рандомизации порядка выполнения. Если задан этот параметр, порядок можно детерминированно воспроизвести при повторном запуске; его указание также включает рандомизацию. Значение должно быть целым числом от 0 до 4294967295. По умолчанию: undefined.
    • rerunFailuresFilePath <string> Путь к файлу, в котором средство запуска тестов будет хранить состояние тестов, чтобы при следующем запуске можно было повторно выполнить только не пройденные тесты. Дополнительные сведения см. в разделе [Повторный запуск не пройденных тестов][]. По умолчанию: undefined.
    • coverage <boolean> Включает сбор данных о покрытии кода. По умолчанию: false.
    • coverageExcludeGlobs <string> | <Array> Исключает определенные файлы из анализа покрытия кода с помощью шаблона glob, который может соответствовать абсолютным и относительным путям к файлам. Это свойство применяется только в том случае, если coverage задано как true. Если указаны и coverageExcludeGlobs, и coverageIncludeGlobs, файлы должны соответствовать обоим критериям, чтобы попасть в отчет о покрытии. По умолчанию: undefined.
    • coverageIncludeGlobs <string> | <Array> Включает определенные файлы в анализ покрытия кода с помощью шаблона glob, который может соответствовать абсолютным и относительным путям к файлам. Это свойство применяется только в том случае, если coverage задано как true. Если указаны и coverageExcludeGlobs, и coverageIncludeGlobs, файлы должны соответствовать обоим критериям, чтобы попасть в отчет о покрытии. По умолчанию: undefined.
    • lineCoverage <number> Задает минимальный процент покрытых строк. Если покрытие кода не достигает указанного порогового значения, процесс завершится с кодом 1. По умолчанию: 0.
    • branchCoverage <number> Задает минимальный процент покрытых ветвей. Если покрытие кода не достигает указанного порогового значения, процесс завершится с кодом 1. По умолчанию: 0.
    • functionCoverage <number> Задает минимальный процент покрытых функций. Если покрытие кода не достигает указанного порогового значения, процесс завершится с кодом 1. По умолчанию: 0.
    • env <Object> Задает переменные среды, передаваемые процессу тестирования. Этот параметр несовместим с isolation='none'. Эти переменные переопределяют переменные основного процесса и не объединяются с process.env. По умолчанию: process.env.
  • Возвращает: <TestsStream>

Примечание: shard используется для горизонтального распараллеливания запуска тестов на нескольких машинах или процессах, что удобно для крупномасштабного выполнения в различных средах. Этот режим несовместим с режимом watch, предназначенным для быстрой итерации при разработке кода за счет автоматического повторного запуска тестов при изменении файлов.

Модули JavaScript
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);
CommonJS
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])

Добавлено в: v22.0.0, v20.13.0
  • name <string> Имя набора тестов, отображаемое при выводе результатов тестирования. По умолчанию: свойство name объекта fn или '<anonymous>', если у fn нет имени.
  • options <Object> Необязательные параметры конфигурации набора тестов. Поддерживаются те же параметры, что и для test([name][, options][, fn]).
  • fn <Function> | <AsyncFunction> Функция набора тестов, объявляющая вложенные тесты и наборы тестов. Первый аргумент этой функции — объект SuiteContext. По умолчанию: функция без действий.
  • Возвращает: <Promise>, немедленно выполненный со значением undefined.

Функция suite() импортируется из модуля node:test.

suite.skip([name][, options][, fn])

Добавлено в: v22.0.0, v20.13.0

Краткая форма для пропуска набора тестов. Эквивалентна suite([name], { skip: true }[, fn]).

suite.todo([name][, options][, fn])

Добавлено в: v22.0.0, v20.13.0

Краткая форма для пометки набора тестов как TODO. Эквивалентна suite([name], { todo: true }[, fn]).

suite.only([name][, options][, fn])

Добавлено в: v22.0.0, v20.13.0

Краткая форма для пометки набора тестов как only. Эквивалентна suite([name], { only: true }[, fn]).

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

Добавлено в: v18.0.0, v16.17.0
История
Версия Изменения
v26.2.0

Добавлен параметр tags.

v20.2.0, v18.17.0

Добавлены сокращённые варианты skip, todo и only.

v18.8.0, v16.18.0

Добавлен параметр signal.

v18.7.0, v16.17.0

Добавлен параметр timeout.

  • name <string> Имя теста, отображаемое при выводе результатов тестирования. По умолчанию: свойство name объекта fn или '<anonymous>', если у fn нет имени.
  • options <Object> Параметры конфигурации теста. Поддерживаются следующие свойства:
    • concurrency <number> | <boolean> Если указано число, асинхронно запускается соответствующее количество тестов (они по-прежнему управляются однопоточным циклом событий). Если значение равно true, все запланированные асинхронные тесты выполняются одновременно в пределах потока. Если значение равно false, одновременно выполняется только один тест. Если значение не указано, вложенные тесты наследуют его от родительского теста. По умолчанию: false.
    • expectFailure <boolean> | <string> | <RegExp> | <Function> | <Object> | <Error> Если значение истинно, ожидается, что тест завершится с ошибкой. Если указана непустая строка, она отображается в результатах тестирования как причина ожидаемой ошибки. Если объект <RegExp> | <Function> | <Object> | <Error> передан непосредственно (без обёртывания в { match: … }), тест проходит только в том случае, если выброшенная ошибка соответствует ему, согласно поведению assert.throws. Чтобы задать и причину, и условие проверки, передайте объект со свойствами label (строка) и match (RegExp, Function, Object или Error). По умолчанию: false.
    • only <boolean> Если значение истинно, а контекст тестирования настроен на запуск тестов only, этот тест будет запущен. В противном случае тест будет пропущен. По умолчанию: false.
    • signal <AbortSignal> Позволяет прервать выполняющийся тест.
    • skip <boolean> | <string> Если значение истинно, тест пропускается. Если указана строка, она отображается в результатах тестирования как причина пропуска теста. По умолчанию: false.
    • tags <string[]> Массив строковых меток, связанных с тестом. Используется вместе с --experimental-test-tag-filter для фильтрации запускаемых тестов. Вложенные тесты наследуют метки наборов тестов путём объединения. См. раздел Метки тестов. По умолчанию: [].
    • todo <boolean> | <string> Если значение истинно, тест помечается как TODO. Если указана строка, она отображается в результатах тестирования как причина, по которой тест TODO. По умолчанию: false.
    • timeout <number> Время в миллисекундах, по истечении которого тест завершится с ошибкой. Если значение не указано, вложенные тесты наследуют его от родительского теста. По умолчанию: Infinity.
    • plan <number> Ожидаемое количество проверок и вложенных тестов, которые должны быть выполнены в тесте. Если количество выполненных проверок не совпадает с указанным в плане, тест завершится с ошибкой. По умолчанию: undefined.
  • fn <Function> | <AsyncFunction> Функция, проверяемая тестом. Первый аргумент этой функции — объект TestContext. Если тест использует обратные вызовы, функция обратного вызова передаётся вторым аргументом. По умолчанию: пустая функция.
  • Возвращает: <Promise> Объект Promise, который выполняется со значением undefined после завершения теста или немедленно, если тест выполняется в наборе тестов.

Функция test() — это значение, импортированное из модуля test. Каждый вызов этой функции приводит к передаче сведений о тесте в <TestsStream>.

Объект TestContext, переданный аргументу fn, можно использовать для выполнения действий, связанных с текущим тестом. Например, чтобы пропустить тест, добавить диагностическую информацию или создать вложенные тесты.

Функция test() возвращает объект Promise, который выполняется после завершения теста. Если test() вызывается в наборе тестов, объект Promise выполняется немедленно. Возвращаемое значение обычно можно не использовать для тестов верхнего уровня. Однако возвращаемое значение вложенных тестов следует использовать, чтобы родительский тест не завершился раньше и не отменил вложенный тест, как показано в следующем примере.

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

Параметр 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])

Псевдоним для suite().

Функция describe() импортируется из модуля node:test.

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

Добавлено в: v19.8.0, v18.15.0

Сокращённая запись для пометки набора тестов как only. Эквивалент describe([name], { only: true }[, fn]).

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

Добавлено в: v18.6.0, v16.17.0
История
Версия Изменения
v19.8.0, v18.16.0

Вызов it() теперь эквивалентен вызову test().

Псевдоним для 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])

Добавлено в: v19.8.0, v18.15.0

Сокращённая запись для пометки теста как only; эквивалент it([name], { only: true }[, fn]).

before([fn][, options])

Добавлено в: v18.8.0, v16.18.0
  • 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', () => {
    // Some relevant assertions here
  });
});
jscopy

after([fn][, options])

Добавлено в: v18.8.0, v16.18.0
  • fn <Function> | <AsyncFunction> Функция-хук. Если хук использует обратные вызовы, функция обратного вызова передаётся вторым аргументом. По умолчанию: пустая функция.
  • options <Object> Параметры конфигурации хука. Поддерживаются следующие свойства:
    • signal <AbortSignal> Позволяет прервать выполняющийся хук.
    • timeout <number> Время в миллисекундах, по истечении которого хук завершится с ошибкой. Если значение не указано, вложенные тесты наследуют его от родительского теста. По умолчанию: Infinity.

Эта функция создаёт хук, который выполняется после запуска набора тестов.

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

Примечание: Гарантируется выполнение хука after, даже если тесты в наборе завершились с ошибкой.

beforeEach([fn][, options])

Добавлено в: v18.8.0, v16.18.0
  • fn <Function> | <AsyncFunction> Функция-хук. Если хук использует обратные вызовы, функция обратного вызова передаётся вторым аргументом. По умолчанию: пустая функция.
  • options <Object> Параметры конфигурации хука. Поддерживаются следующие свойства:
    • signal <AbortSignal> Позволяет прервать выполняющийся хук.
    • timeout <number> Время в миллисекундах, по истечении которого хук завершится с ошибкой. Если значение не указано, вложенные тесты наследуют его от родительского теста. По умолчанию: Infinity.

Эта функция создаёт хук, который выполняется перед каждым тестом в текущем наборе.

describe('tests', async () => {
  beforeEach(() => console.log('about to run a test'));
  it('is a subtest', () => {
    // Some relevant assertion here
  });
});
jscopy

afterEach([fn][, options])

Добавлено в: v18.8.0, v16.18.0
  • fn <Function> | <AsyncFunction> Функция-хук. Если хук использует обратные вызовы, функция обратного вызова передаётся вторым аргументом. По умолчанию: пустая функция.
  • options <Object> Параметры конфигурации хука. Поддерживаются следующие свойства:
    • signal <AbortSignal> Позволяет прервать выполняющийся хук.
    • timeout <number> Время в миллисекундах, по истечении которого хук завершится с ошибкой. Если значение не указано, вложенные тесты наследуют его от родительского теста. По умолчанию: Infinity.

Эта функция создаёт хук, который выполняется после каждого теста в текущем наборе. Хук afterEach() выполняется даже в случае ошибки теста.

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

assert

Добавлено в: v23.7.0, v22.14.0

Объект, методы которого используются для настройки доступных проверок в объектах TestContext текущего процесса. Методы из node:assert и функции тестирования снимков доступны по умолчанию.

Можно применить одну и ту же конфигурацию ко всем файлам, поместив общий код конфигурации в модуль, предварительно загруженный с помощью --require или --import.

assert.register(name, fn)

Добавлено в: v23.7.0, v22.14.0

Определяет новую функцию проверки с указанными именем и функцией. Если проверка с таким именем уже существует, она перезаписывается.

snapshot

Добавлено в: v22.3.0

Объект, методы которого используются для настройки параметров снимков по умолчанию в текущем процессе. Можно применить одну и ту же конфигурацию ко всем файлам, поместив общий код конфигурации в модуль, предварительно загруженный с помощью --require или --import.

snapshot.setDefaultSnapshotSerializers(serializers)

Добавлено в: v22.3.0
  • serializers <Array> Массив синхронных функций, используемых в качестве сериализаторов по умолчанию для тестов снимков.

Эта функция используется для настройки механизма сериализации по умолчанию, применяемого средством запуска тестов. По умолчанию средство запуска тестов выполняет сериализацию, вызывая JSON.stringify(value, null, 2) для переданного значения. У JSON.stringify() есть ограничения, связанные с циклическими структурами и поддерживаемыми типами данных. Если требуется более надёжный механизм сериализации, следует использовать эту функцию.

snapshot.setResolveSnapshotPath(fn)

Добавлено в: v22.3.0
  • fn <Function> Функция, вычисляющая расположение файла снимка. Функция получает путь к файлу теста в качестве единственного аргумента. Если тест не связан с файлом (например, в REPL), входное значение будет undefined. fn() должна возвращать строку, указывающую расположение файла снимка.

Эта функция используется для настройки расположения файла снимка, применяемого при тестировании снимков. По умолчанию имя файла снимка совпадает с именем файла точки входа, но имеет расширение .snapshot.

Класс: MockFunctionContext

Добавлено в: v19.1.0, v18.13.0

Класс MockFunctionContext используется для проверки или изменения поведения моков, созданных с помощью API MockTracker.

ctx.calls

Добавлено в: v19.1.0, v18.13.0
  • Тип: <Array>

Геттер, возвращающий копию внутреннего массива, используемого для отслеживания вызовов мока. Каждый элемент массива — это объект со следующими свойствами.

  • arguments <Array> Массив аргументов, переданных функции-моку.
  • error <any> Если функция-мок выбросила исключение, это свойство содержит выброшенное значение. По умолчанию: undefined.
  • result <any> Значение, возвращённое функцией-моком.
  • stack <Error> Объект Error, стек вызовов которого можно использовать для определения места вызова функции-мока.
  • target <Function> | <undefined> Если функция-мок является конструктором, это поле содержит создаваемый класс. В противном случае его значение будет undefined.
  • this <any> Значение this функции-мока.

ctx.callCount()

Добавлено в: v19.1.0, v18.13.0
  • Возвращает: <integer> Количество вызовов этого мока.

Эта функция возвращает количество вызовов этого мока. Она эффективнее проверки ctx.calls.length, поскольку ctx.calls — это геттер, создающий копию внутреннего массива учёта вызовов.

ctx.mockImplementation(implementation)

Добавлено в: v19.1.0, v18.13.0
  • implementation <Function> | <AsyncFunction> Функция, которая будет использоваться в качестве новой реализации мока.

Эта функция используется для изменения поведения существующего мока.

В следующем примере с помощью 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);
});
jscopy

ctx.mockImplementationOnce(implementation[, onCall])

Добавлено в: v19.1.0, v18.13.0
  • 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);
});
jscopy

ctx.resetCalls()

Добавлено в: v19.3.0, v18.13.0

Сбрасывает историю вызовов функции-мока.

ctx.restore()

Добавлено в: v19.1.0, v18.13.0

Сбрасывает реализацию функции-мока, возвращая её к исходному поведению. После вызова этой функции мок по-прежнему можно использовать.

Класс: MockModuleContext

Добавлено в: v22.3.0, v20.18.0

Стабильность: 1.0 — ранняя разработка

Класс MockModuleContext используется для изменения поведения моков модулей, созданных с помощью API MockTracker.

ctx.restore()

Добавлено в: v22.3.0, v20.18.0

Сбрасывает реализацию модуля-мока.

Класс: MockPropertyContext

Добавлено в: v24.3.0, v22.20.0

Класс MockPropertyContext используется для проверки или изменения поведения моков свойств, созданных с помощью API MockTracker.

ctx.accesses

  • Тип: <Array>

Геттер, возвращающий копию внутреннего массива, используемого для отслеживания обращений (чтения/записи) к свойству-моку. Каждый элемент массива — это объект со следующими свойствами:

  • type <string> Либо 'get', либо 'set', указывающее тип обращения.
  • value <any> Прочитанное (для 'get') или записанное (для 'set') значение.
  • stack <Error> Объект Error, стек вызовов которого можно использовать для определения места вызова функции-мока.

ctx.accessCount()

  • Возвращает: <integer> Количество обращений к свойству (чтений или записей).

Эта функция возвращает количество обращений к свойству. Она эффективнее проверки ctx.accesses.length, поскольку ctx.accesses — это геттер, создающий копию внутреннего массива учёта обращений.

ctx.mockImplementation(value)

  • value <any> Новое значение свойства-мока.

Эта функция используется для изменения значения, возвращаемого геттером свойства-мока.

ctx.mockImplementationOnce(value[, onAccess])

  • value <any> Значение, которое будет использоваться в качестве реализации мока для обращения с номером, указанным в onAccess.
  • onAccess <integer> Номер обращения, для которого будет использоваться value. Если указанное обращение уже состоялось, будет выброшено исключение. По умолчанию: номер следующего обращения.

Эта функция используется для изменения поведения существующего мока при одном обращении. После обращения под номером onAccess мок вернётся к поведению, которое использовалось бы, если бы не был вызван mockImplementationOnce().

В следующем примере с помощью t.mock.property() создаётся функция-мок и вызывается свойство-мок, затем реализация мока для следующего обращения заменяется другим значением, после чего восстанавливается прежнее поведение.

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);
});
jscopy
Предупреждение

Для единообразия с остальной частью API мокирования эта функция считает обращениями как чтение, так и запись свойств. Если запись свойства происходит при том же индексе обращения, значение «once» будет использовано операцией записи, а значение свойства-мока изменится на значение «once». Это может привести к неожиданному поведению, если предполагается, что значение «once» будет использоваться только при чтении свойства.

ctx.resetAccesses()

Сбрасывает историю обращений к свойству-моку.

ctx.restore()

Сбрасывает реализацию свойства-мока, возвращая её к исходному поведению. После вызова этой функции мок по-прежнему можно использовать.

Class: MockTracker

Добавлено в: v19.1.0, v18.13.0

Класс MockTracker используется для управления функциями создания имитаций. Модуль запуска тестов предоставляет экспорт верхнего уровня mock, который является экземпляром MockTracker. Каждый тест также предоставляет собственный экземпляр MockTracker через свойство mock контекста теста.

mock.fn([original[, implementation]][, options])

Добавлено в: v19.1.0, v18.13.0
  • original <Function> | <AsyncFunction> Необязательная функция, для которой создается имитация. По умолчанию: функция-заглушка.
  • implementation <Function> | <AsyncFunction> Необязательная функция, используемая в качестве реализации имитации для original. Это удобно для создания имитаций, которые ведут себя одним образом заданное число вызовов, а затем восстанавливают поведение original. По умолчанию: функция, указанная в original.
  • options <Object> Необязательные параметры конфигурации функции-имитации. Поддерживаются следующие свойства:
    • times <integer> Количество раз, которое имитация будет использовать поведение implementation. После того как функция-имитация будет вызвана times раз, она автоматически восстановит поведение original. Значение должно быть целым числом больше нуля. По умолчанию: Infinity.
  • Возвращает: <Proxy> Функцию-имитацию. Функция-имитация содержит специальное свойство mock, являющееся экземпляром MockFunctionContext; его можно использовать для проверки и изменения поведения функции-имитации.

Эта функция используется для создания функции-имитации.

В следующем примере создается функция-имитация, которая увеличивает счетчик на единицу при каждом вызове. Параметр 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);
});
jscopy

mock.getter(object, methodName[, implementation][, options])

Добавлено в: v19.3.0, v18.13.0

Эта функция является синтаксическим сокращением для MockTracker.method, где options.getter установлено в true.

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

Добавлено в: v19.1.0, v18.13.0
  • object <Object> Объект, метод которого имитируется.
  • methodName <string> | <symbol> Идентификатор метода объекта object, для которого создается имитация. Если object[methodName] не является функцией, будет выброшена ошибка.
  • implementation <Function> | <AsyncFunction> Необязательная функция, используемая в качестве реализации имитации для object[methodName]. По умолчанию: исходный метод, указанный в object[methodName].
  • options <Object> Необязательные параметры конфигурации метода-имитации. Поддерживаются следующие свойства:
    • getter <boolean> Если true, object[methodName] рассматривается как геттер. Этот параметр нельзя использовать вместе с параметром setter. По умолчанию: false.
    • setter <boolean> Если true, object[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.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);
});
jscopy

mock.module(specifier[, options])

Добавлено в: v22.3.0, v20.18.0
История
Версия Изменения
v24.0.0, v22.17.0

Добавлена поддержка модулей JSON.

Стабильность: 1.0 — ранняя стадия разработки

  • specifier <string> | <URL> Строка, идентифицирующая модуль, для которого создается имитация.
  • options <Object> Необязательные параметры конфигурации имитации модуля. Поддерживаются следующие свойства:
    • cache <boolean> Если false, каждый вызов require() или import() создает новую имитацию модуля. Если true, последующие вызовы возвращают ту же имитацию модуля, которая добавляется в кэш CommonJS. По умолчанию: false.
    • exports <Object> Необязательные имитируемые экспорты. Свойство default, если оно указано, используется в качестве экспорта по умолчанию имитируемого модуля. Все остальные собственные перечисляемые свойства используются как именованные экспорты. Этот параметр нельзя использовать вместе с defaultExport или namedExports.
      • Если имитируется модуль CommonJS или встроенный модуль, exports.default используется как значение module.exports.
      • Если для имитации модуля CommonJS или встроенного модуля не задано exports.default, по умолчанию module.exports равен пустому объекту.
      • Если именованные экспорты заданы вместе с экспортом по умолчанию, не являющимся объектом, имитация выбрасывает исключение при использовании в качестве модуля CommonJS или встроенного модуля.
    • defaultExport <any> Необязательное значение, используемое в качестве экспорта по умолчанию имитируемого модуля. Если это значение не задано, имитации ESM не содержат экспорта по умолчанию. Если имитируется модуль CommonJS или встроенный модуль, это значение используется в качестве значения module.exports. Если это значение не задано, для имитаций CJS и встроенных модулей в качестве значения module.exports используется пустой объект. Этот параметр нельзя использовать вместе с options.exports. Этот параметр устарел и будет удален в одной из будущих версий. Предпочтительно использовать options.exports.default.
    • namedExports <Object> Необязательный объект, ключи и значения которого используются для создания именованных экспортов имитируемого модуля. Если имитируется модуль CommonJS или встроенный модуль, эти значения копируются в module.exports. Поэтому, если имитация создана с именованными экспортами и экспортом по умолчанию, не являющимся объектом, при использовании в качестве модуля CJS или встроенного модуля она выбросит исключение. Этот параметр нельзя использовать вместе с options.exports. Этот параметр устарел и будет удален в одной из будущих версий. Предпочтительно использовать options.exports.
  • Возвращает: <MockModuleContext> Объект, который можно использовать для управления имитацией.

Эта функция используется для имитации экспортов модулей ECMAScript, модулей CommonJS, модулей JSON и встроенных модулей Node.js. Ссылки на исходный модуль, полученные до создания имитации, не затрагиваются. Чтобы включить имитацию модулей, Node.js необходимо запустить с флагом командной строки --experimental-test-module-mocks.

Примечание: хуки настройки модулей, зарегистрированные через хуки настройки модулей с помощью синхронного API, влияют на разрешение specifier, переданного в mock.module. Хуки настройки, зарегистрированные через асинхронный API, в настоящее время игнорируются (поскольку загрузчик исполнителя тестов синхронный, а node не поддерживает загрузку с несколькими цепочками / между цепочками).

В следующем примере показано, как создать имитацию модуля.

test('mocks a builtin module in both module systems', async (t) => {
  // Create a mock of 'node:readline' with a named export named 'foo', which
  // does not exist in the original 'node:readline' module.
  const mock = t.mock.module('node:readline', {
    exports: { foo: () => 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);
});
jscopy

mock.property(object, propertyName[, value])

Добавлено в: v24.3.0, v22.20.0
  • object <Object> Объект, значение свойства которого имитируется.
  • propertyName <string> | <symbol> Идентификатор свойства объекта object, для которого создается имитация.
  • value <any> Необязательное значение, используемое в качестве имитируемого значения для object[propertyName]. По умолчанию: исходное значение свойства.
  • Возвращает: <Proxy> Прокси для имитируемого объекта. Имитируемый объект содержит специальное свойство mock, являющееся экземпляром MockPropertyContext; его можно использовать для проверки и изменения поведения имитируемого свойства.

Создает имитацию значения свойства объекта. Это позволяет отслеживать и контролировать доступ к определенному свойству, в том числе количество его чтений (геттер) и записей (сеттер), а также восстанавливать исходное значение после имитации.

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

mock.reset()

Добавлено в: v19.1.0, v18.13.0

Эта функция восстанавливает поведение по умолчанию всех имитаций, ранее созданных этим MockTracker, и отменяет их привязку к экземпляру MockTracker. После отмены привязки имитации по-прежнему можно использовать, однако экземпляр MockTracker больше нельзя использовать для сброса их поведения или иного взаимодействия с ними.

После завершения каждого теста эта функция вызывается для MockTracker контекста теста. Если глобальный MockTracker используется активно, рекомендуется вызывать эту функцию вручную.

mock.restoreAll()

Добавлено в: v19.1.0, v18.13.0

Эта функция восстанавливает поведение по умолчанию всех имитаций, ранее созданных этим MockTracker. В отличие от mock.reset(), mock.restoreAll() не отменяет привязку имитаций к экземпляру MockTracker.

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

Добавлено в: v19.3.0, v18.13.0

Эта функция является синтаксическим сокращением для MockTracker.method, где options.setter установлено в true.

Class: MockTimers

Добавлено в: v20.4.0, v18.19.0
История
Версия Изменения
v23.1.0

Имитация таймеров теперь имеет стабильный статус.

Имитация таймеров — это широко используемый в тестировании программного обеспечения метод симуляции и управления поведением таймеров, таких как setInterval и setTimeout, без фактического ожидания заданных временных интервалов.

MockTimers также может имитировать объект Date.

MockTracker предоставляет экспорт верхнего уровня timers, который является экземпляром MockTimers.

timers.enable([enableOptions])

Добавлено в: v20.4.0, v18.19.0
История
Версия Изменения
v21.2.0, v20.11.0

Параметры обновлены: теперь это объект параметров с доступными API и начальной эпохой по умолчанию.

Включает имитацию указанных таймеров.

  • 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 повлияет на поведение имитируемых таймеров, поскольку они используют одни и те же внутренние часы.

Пример использования без задания начального времени:

Модули JavaScript
import { mock } from 'node:test';
mock.timers.enable({ apis: ['setInterval'] });
CommonJS
const { mock } = require('node:test');
mock.timers.enable({ apis: ['setInterval'] });

В приведенном выше примере включается имитация таймера setInterval и неявно имитируется функция clearInterval. Имитируются только функции setInterval и clearInterval из node:timers, node:timers/promises и globalThis.

Пример использования с заданным начальным временем

Модули JavaScript
import { mock } from 'node:test';
mock.timers.enable({ apis: ['Date'], now: 1000 });
CommonJS
const { mock } = require('node:test');
mock.timers.enable({ apis: ['Date'], now: 1000 });

Пример использования с объектом Date, заданным в качестве начального времени

Модули JavaScript
import { mock } from 'node:test';
mock.timers.enable({ apis: ['Date'], now: new Date() });
CommonJS
const { mock } = require('node:test');
mock.timers.enable({ apis: ['Date'], now: new Date() });

Кроме того, если вызвать mock.timers.enable() без параметров:

Будут имитироваться все таймеры ('setInterval', 'clearInterval', 'setTimeout', 'clearTimeout', 'setImmediate' и 'clearImmediate'). Также будут имитироваться функции setInterval, clearInterval, setTimeout, clearTimeout, setImmediate и clearImmediate из node:timers, node:timers/promises и globalThis, а также глобальный объект Date.

timers.reset()

Добавлено в: v20.4.0, v18.19.0

Эта функция восстанавливает поведение по умолчанию всех имитаций, ранее созданных этим экземпляром MockTimers, и отменяет привязку имитаций к экземпляру MockTracker.

Примечание: После завершения каждого теста эта функция вызывается для MockTracker контекста теста.

Модули JavaScript
import { mock } from 'node:test';
mock.timers.reset();
CommonJS
const { mock } = require('node:test');
mock.timers.reset();

timers[Symbol.dispose]()

Вызывает timers.reset().

timers.tick([milliseconds])

Добавлено в: v20.4.0, v18.19.0

Продвигает время для всех имитируемых таймеров.

  • milliseconds <number> Время в миллисекундах, на которое нужно продвинуть таймеры. По умолчанию: 1.

Примечание: Это отличается от поведения setTimeout в Node.js: принимаются только положительные числа. В Node.js setTimeout с отрицательными числами поддерживается только для совместимости с веб-платформой.

В следующем примере имитируется функция setTimeout, а вызов .tick продвигает время, запуская все ожидающие таймеры.

Модули JavaScript
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);
});
CommonJS
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 можно вызывать много раз.

Модули JavaScript
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);
});
CommonJS
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 также настроен для имитации).

Модули JavaScript
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);
});
CommonJS
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);
});
Использование функций очистки

Как уже упоминалось, все функции очистки таймеров (clearTimeout, clearIntervalи clearImmediate) неявно имитируются. Рассмотрим следующий пример с использованием setTimeout:

Модули JavaScript
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);
});
CommonJS
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

После включения имитации таймеров активируются модули node:timers, node:timers/promises и таймеры из глобального контекста Node.js:

Примечание: Деструктуризация функций, таких как import { setTimeout } from 'node:timers', в настоящее время этим API не поддерживается.

Модули JavaScript
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);
});
CommonJS
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 setInterval из node:timers/promises является AsyncGenerator и также поддерживается этим API:

Модули JavaScript
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));
  }
});
CommonJS
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()

Добавлено в: v20.4.0, v18.19.0

Немедленно запускает все ожидающие имитируемые таймеры. Если объект Date также имитируется, он также продвинет объект Date до времени самого позднего таймера.

Пример ниже немедленно запускает все ожидающие таймеры, заставляя их выполниться без задержки.

Модули JavaScript
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);
});
CommonJS
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)

Добавлено в: v21.2.0, v20.11.0

Задает текущую метку времени Unix, которая будет использоваться в качестве ориентира для любых имитируемых объектов Date.

Модули JavaScript
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);
});
CommonJS
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);
});
Совместная работа дат и таймеров

Объекты дат и таймеров зависят друг от друга. Если использовать setTime() для передачи текущего времени имитируемому объекту Date, установленные таймеры с помощью setTimeout и setInterval не будут затронуты.

Однако метод tick будет продвигать время имитируемого объекта Date.

Модули JavaScript
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);
});
CommonJS
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);
});

Class: TestsStream

Добавлено в: v18.9.0, v16.19.0
История
Версия Изменения
v26.3.0

Добавлен parentId для тестирования событий, содержащих testId.

v20.0.0, v19.9.0, v18.17.0

Для событий test:pass и test:fail добавлен тип, указывающий, что тест является набором тестов.

  • Наследует <Readable>

При успешном вызове метода 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> Процент покрытых функций.
        • functions <Array> Массив функций, представляющий покрытие функций.
          • name <string> Имя функции.
          • line <number> Номер строки, в которой определена функция.
          • count <number> Количество вызовов функции.
        • branches <Array> Массив ветвей, представляющий покрытие ветвей.
          • line <number> Номер строки, в которой определена ветвь.
          • count <number> Количество прохождений по ветви.
        • lines <Array> Массив строк, представляющий номера строк и количество их покрытий.
          • line <number> Номер строки.
          • count <number> Количество покрытий строки.
      • thresholds <Object> Объект, содержащий сведения о том, достигнуты ли пороговые значения для каждого типа покрытия.
        • function <number> Пороговое значение покрытия функций.
        • branch <number> Пороговое значение покрытия ветвей.
        • line <number> Пороговое значение покрытия строк.
      • 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:complete'

  • data <Object>
    • column <number> | <undefined> Номер столбца, в котором определен тест, или undefined, если тест был запущен через REPL.
    • details <Object> Дополнительные метаданные выполнения.
      • passed <boolean> Успешно ли прошел тест.
      • duration_ms <number> Длительность теста в миллисекундах.
      • error <Error> | <undefined> Ошибка, оборачивающая ошибку, выброшенную тестом, если он завершился неудачно.
        • cause <Error> Фактическая ошибка, выброшенная тестом.
      • type <string> | <undefined> Тип теста, указывающий, является ли он набором тестов.
    • file <string> | <undefined> Путь к файлу теста; undefined, если тест был запущен через REPL.
    • line <number> | <undefined> Номер строки, в которой определен тест, или undefined, если тест был запущен через REPL.
    • name <string> Имя теста.
    • nesting <number> Уровень вложенности теста.
    • parentId <number> | <undefined> testId родительского теста или undefined для тестов верхнего уровня. Позволяет пользовательским репортерам отслеживать иерархию, когда параллельные соседние тесты одного уровня вложенности чередуются.
    • tags <string[]> Объединенные теги в нижнем регистре, объявленные для теста и его родительских наборов тестов, в порядке объявления. Для тестов без тегов значение пустое. См. Теги тестов.
    • testId <number> Числовой идентификатор экземпляра этого теста, уникальный в процессе файла теста. Одинаков для всех событий одного экземпляра теста, что позволяет надежно сопоставлять их в пользовательских репортерах.
    • testNumber <number> Порядковый номер теста.
    • todo <string> | <boolean> | <undefined> Присутствует, если вызывается context.todo
    • skip <string> | <boolean> | <undefined> Присутствует, если вызывается context.skip

Генерируется после завершения выполнения теста. Это событие генерируется не в том же порядке, в котором определены тесты. Соответствующие события в порядке объявления: 'test:pass' и 'test:fail'.

Событие: 'test:dequeue'

  • data <Object>
    • column <number> | <undefined> Номер столбца, в котором определен тест, или undefined, если тест был запущен через REPL.
    • file <string> | <undefined> Путь к файлу теста; undefined, если тест был запущен через REPL.
    • line <number> | <undefined> Номер строки, в которой определен тест, или undefined, если тест был запущен через REPL.
    • name <string> Имя теста.
    • nesting <number> Уровень вложенности теста.
    • parentId <number> | <undefined> testId родительского теста или undefined для тестов верхнего уровня. Позволяет пользовательским репортерам отслеживать иерархию, когда параллельные соседние тесты одного уровня вложенности чередуются.
    • tags <string[]> Объединенные теги в нижнем регистре, объявленные для теста и его родительских наборов тестов, в порядке объявления. Для тестов без тегов значение пустое. См. Теги тестов.
    • testId <number> Числовой идентификатор экземпляра этого теста, уникальный в процессе файла теста. Одинаков для всех событий одного экземпляра теста, что позволяет надежно сопоставлять их в пользовательских репортерах.
    • type <string> Тип теста: 'suite' или 'test'.

Генерируется, когда тест извлекается из очереди непосредственно перед выполнением. Не гарантируется, что это событие будет сгенерировано в том же порядке, в котором определены тесты. Соответствующее событие в порядке объявления: 'test:start'.

Событие: 'test:diagnostic'

  • data <Object>
    • column <number> | <undefined> Номер столбца, в котором определен тест, или undefined, если тест был запущен через REPL.
    • file <string> | <undefined> Путь к файлу теста; undefined, если тест был запущен через REPL.
    • line <number> | <undefined> Номер строки, в которой определен тест, или undefined, если тест был запущен через REPL.
    • message <string> Диагностическое сообщение.
    • nesting <number> Уровень вложенности теста.
    • level <string> Уровень важности диагностического сообщения. Возможные значения:
      • 'info': информационные сообщения.
      • 'warn': предупреждения.
      • 'error': ошибки.

Генерируется при вызове context.diagnostic. Гарантируется, что это событие будет сгенерировано в том же порядке, в котором определены тесты.

Событие: 'test:enqueue'

  • data <Object>
    • column <number> | <undefined> Номер столбца, в котором определен тест, или undefined, если тест был запущен через REPL.
    • file <string> | <undefined> Путь к файлу теста; undefined, если тест был запущен через REPL.
    • line <number> | <undefined> Номер строки, в которой определен тест, или undefined, если тест был запущен через REPL.
    • name <string> Имя теста.
    • nesting <number> Уровень вложенности теста.
    • parentId <number> | <undefined> testId родительского теста или undefined для тестов верхнего уровня. Позволяет пользовательским репортерам отслеживать иерархию, когда параллельные соседние тесты одного уровня вложенности чередуются.
    • tags <string[]> Объединенные теги в нижнем регистре, объявленные для теста и его родительских наборов тестов, в порядке объявления. Для тестов без тегов значение пустое. См. Теги тестов.
    • testId <number> Числовой идентификатор экземпляра этого теста, уникальный в процессе файла теста. Одинаков для всех событий одного экземпляра теста, что позволяет надежно сопоставлять их в пользовательских репортерах.
    • type <string> Тип теста: 'suite' или 'test'.

Генерируется, когда тест ставится в очередь на выполнение.

Событие: 'test:fail'

  • data <Object>
    • column <number> | <undefined> Номер столбца, в котором определен тест, или undefined, если тест был запущен через REPL.
    • details <Object> Дополнительные метаданные выполнения.
      • duration_ms <number> Длительность теста в миллисекундах.
      • error <Error> Ошибка, оборачивающая ошибку, выброшенную тестом.
        • cause <Error> Фактическая ошибка, выброшенная тестом.
      • type <string> | <undefined> Тип теста, указывающий, является ли он набором тестов.
      • attempt <number> | <undefined> Номер попытки запуска теста; присутствует только при использовании флага --test-rerun-failures.
    • file <string> | <undefined> Путь к файлу теста; undefined, если тест был запущен через REPL.
    • line <number> | <undefined> Номер строки, в которой определен тест, или undefined, если тест был запущен через REPL.
    • name <string> Имя теста.
    • nesting <number> Уровень вложенности теста.
    • parentId <number> | <undefined> testId родительского теста или undefined для тестов верхнего уровня. Позволяет пользовательским репортерам отслеживать иерархию, когда параллельные соседние тесты одного уровня вложенности чередуются.
    • tags <string[]> Объединенные теги в нижнем регистре, объявленные для теста и его родительских наборов тестов, в порядке объявления. Для тестов без тегов значение пустое. См. Теги тестов.
    • testId <number> Числовой идентификатор экземпляра этого теста, уникальный в процессе файла теста. Одинаков для всех событий одного экземпляра теста, что позволяет надежно сопоставлять их в пользовательских репортерах.
    • testNumber <number> Порядковый номер теста.
    • todo <string> | <boolean> | <undefined> Присутствует, если вызывается context.todo
    • skip <string> | <boolean> | <undefined> Присутствует, если вызывается context.skip

Генерируется при неудачном завершении теста. Гарантируется, что это событие будет сгенерировано в том же порядке, в котором определены тесты. Соответствующее событие в порядке выполнения: 'test:complete'.

Событие: 'test:interrupted'

Добавлено в: v25.7.0
  • data <Object>
    • tests <Array> Массив объектов, содержащих сведения о прерванных тестах.
      • column <number> | <undefined> Номер столбца, в котором определен тест, или undefined, если тест был запущен через REPL.
      • file <string> | <undefined> Путь к файлу теста; undefined, если тест был запущен через REPL.
      • line <number> | <undefined> Номер строки, в которой определен тест, или undefined, если тест был запущен через REPL.
      • name <string> Имя теста.
      • nesting <number> Уровень вложенности теста.

Генерируется, когда выполнение тестов прерывается сигналом SIGINT (например, при нажатии Ctrl+C). Событие содержит сведения о тестах, выполнявшихся в момент прерывания.

При использовании изоляции процессов (по умолчанию) именем теста будет путь к файлу, поскольку родительский исполнитель тестов знает только о тестах уровня файла. При использовании --test-isolation=none отображается фактическое имя теста.

Событие: 'test:pass'

  • data <Object>
    • column <number> | <undefined> Номер столбца, в котором определён тест, или undefined, если тест был запущен через REPL.
    • 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> Путь к файлу теста; undefined, если тест был запущен через REPL.
    • line <number> | <undefined> Номер строки, в которой определён тест, или undefined, если тест был запущен через REPL.
    • name <string> Имя теста.
    • nesting <number> Уровень вложенности теста.
    • parentId <number> | <undefined> testId родительского теста или undefined для тестов верхнего уровня. Позволяет пользовательским репортерам отслеживать родство, когда параллельные тесты-соседи одного уровня вложенности чередуются.
    • tags <string[]> Уплощённый список тегов в нижнем регистре, объявленных для теста и его родительских наборов тестов, в порядке объявления. Для тестов без тегов список пуст. См. Теги тестов.
    • testId <number> Числовой идентификатор экземпляра теста, уникальный в процессе файла теста. Одинаков для всех событий одного экземпляра теста, что позволяет надёжно сопоставлять их в пользовательских репортерах.
    • testNumber <number> Порядковый номер теста.
    • todo <string> | <boolean> | <undefined> Присутствует, если вызван context.todo
    • skip <string> | <boolean> | <undefined> Присутствует, если вызван context.skip

Генерируется, когда тест проходит. Гарантируется, что это событие будет сгенерировано в том же порядке, в котором определены тесты. Соответствующее событие, упорядоченное по выполнению, — 'test:complete'.

Событие: 'test:plan'

  • data <Object>
    • column <number> | <undefined> Номер столбца, в котором определён тест, или undefined, если тест был запущен через REPL.
    • file <string> | <undefined> Путь к файлу теста; undefined, если тест был запущен через REPL.
    • line <number> | <undefined> Номер строки, в которой определён тест, или undefined, если тест был запущен через REPL.
    • nesting <number> Уровень вложенности теста.
    • count <number> Количество выполненных подтестов.

Генерируется, когда все подтесты указанного теста завершены. Гарантируется, что это событие будет сгенерировано в том же порядке, в котором определены тесты.

Событие: 'test:start'

  • data <Object>
    • column <number> | <undefined> Номер столбца, в котором определён тест, или undefined, если тест был запущен через REPL.
    • file <string> | <undefined> Путь к файлу теста; undefined, если тест был запущен через REPL.
    • line <number> | <undefined> Номер строки, в которой определён тест, или undefined, если тест был запущен через REPL.
    • name <string> Имя теста.
    • nesting <number> Уровень вложенности теста.
    • parentId <number> | <undefined> testId родительского теста или undefined для тестов верхнего уровня. Позволяет пользовательским репортерам отслеживать родство, когда параллельные тесты-соседи одного уровня вложенности чередуются.
    • tags <string[]> Уплощённый список тегов в нижнем регистре, объявленных для теста и его родительских наборов тестов, в порядке объявления. Для тестов без тегов список пуст. См. Теги тестов.
    • testId <number> Числовой идентификатор экземпляра теста, уникальный в процессе файла теста. Одинаков для всех событий одного экземпляра теста, что позволяет надёжно сопоставлять их в пользовательских репортерах.

Генерируется, когда тест начинает сообщать о своём статусе и статусе своих подтестов. Гарантируется, что это событие будет сгенерировано в том же порядке, в котором определены тесты. Соответствующее событие, упорядоченное по выполнению, — 'test:dequeue'.

Событие: 'test:stderr'

  • data <Object>
    • file <string> Путь к файлу теста.
    • message <string> Сообщение, записанное в stderr.

Генерируется, когда выполняющийся тест записывает данные в stderr. Это событие генерируется только при передаче флага --test. Не гарантируется, что это событие будет сгенерировано в том же порядке, в котором определены тесты.

Событие: 'test:stdout'

  • data <Object>
    • file <string> Путь к файлу теста.
    • message <string> Сообщение, записанное в stdout.

Генерируется, когда выполняющийся тест записывает данные в stdout. Это событие генерируется только при передаче флага --test. Не гарантируется, что это событие будет сгенерировано в том же порядке, в котором определены тесты.

Событие: 'test:summary'

  • data <Object>
    • counts <Object> Объект, содержащий количество различных результатов тестирования.
      • cancelled <number> Общее количество отменённых тестов.
      • failed <number> Общее количество не пройденных тестов.
      • passed <number> Общее количество пройденных тестов.
      • skipped <number> Общее количество пропущенных тестов.
      • suites <number> Общее количество выполненных наборов тестов.
      • tests <number> Общее количество выполненных тестов без учёта наборов тестов.
      • todo <number> Общее количество тестов TODO.
      • topLevel <number> Общее количество тестов и наборов тестов верхнего уровня.
    • duration_ms <number> Продолжительность запуска тестов в миллисекундах.
    • file <string> | <undefined> Путь к файлу теста, для которого сформирована сводка. Если сводка относится к нескольким файлам, это значение равно undefined.
    • success <boolean> Указывает, считается ли запуск тестов успешным. Если возникло какое-либо условие ошибки, например не пройденный тест или недостигнутый порог покрытия, этому значению присваивается false.

Генерируется по завершении запуска тестов. Это событие содержит показатели завершённого запуска и полезно для определения того, прошёл запуск тестов успешно или нет. При использовании изоляции тестов на уровне процесса событие 'test:summary' генерируется для каждого файла теста, а также формируется итоговая сводка.

Событие: 'test:watch:drained'

Генерируется, когда в режиме наблюдения больше не остаётся тестов в очереди на выполнение.

Событие: 'test:watch:restarted'

Генерируется, когда в режиме наблюдения один или несколько тестов перезапускаются из-за изменения файла.

getTestContext()

Добавлено в: v26.1.0
  • Возвращает: <undefined>

Возвращает объект TestContext или SuiteContext, связанный с выполняющимся в данный момент тестом или набором тестов, либо undefined, если вызван вне теста или набора тестов. Эта функция позволяет получить доступ к информации о контексте из функции теста или набора тестов, а также из любых асинхронных операций внутри них.

import { getTestContext } from 'node:test';

test('example test', async () => {
  const ctx = getTestContext();
  console.log(`Running test: ${ctx.name}`);
});

describe('example suite', () => {
  const ctx = getTestContext();
  console.log(`Running suite: ${ctx.name}`);
});
mjscopy

При вызове из теста возвращает TestContext. При вызове из набора тестов возвращает SuiteContext.

При вызове вне теста или набора тестов (например, на верхнем уровне модуля или в обратном вызове setTimeout после завершения выполнения) эта функция возвращает undefined.

При вызове из хука (before, beforeEach, after, afterEach) функция возвращает контекст теста или набора тестов, с которым связан этот хук.

Инструментирование тестов и OpenTelemetry

Добавлено в: v26.1.0

Средство запуска тестов публикует события выполнения тестов через модуль Node.js diagnostics_channel, что позволяет интегрировать его со средствами наблюдаемости, такими как OpenTelemetry, без необходимости изменять само средство запуска тестов.

События трассировки

Средство запуска тестов публикует события в канал трассировки 'node.test'. Подписчики могут использовать API TracingChannel для привязки контекста или выполнения пользовательского инструментирования.

Канал: 'tracing:node.test:start'
  • data <Object>
    • name <string> Имя теста.
    • nesting <number> Уровень вложенности теста.
    • file <string> | <undefined> Путь к файлу теста или undefined при запуске в REPL.
    • type <string> Тип теста. Может быть 'test' или 'suite'.

Генерируется при запуске теста или набора тестов. Диапазон трассировки теста охватывает все его хуки before, beforeEach и afterEach, а также тело теста.

Канал: 'tracing:node.test:end'
  • data <Object>
    • name <string> Имя теста.
    • nesting <number> Уровень вложенности теста.
    • file <string> | <undefined> Путь к файлу теста или undefined при запуске в REPL.
    • type <string> Тип теста. Может быть 'test' или 'suite'.

Генерируется по завершении теста или набора тестов.

Канал: 'tracing:node.test:error'
  • data <Object>
    • name <string> Имя теста.
    • nesting <number> Уровень вложенности теста.
    • file <string> | <undefined> Путь к файлу теста или undefined при запуске в REPL.
    • type <string> Тип теста. Может быть 'test' или 'suite'.
    • error <Error> Возникшая ошибка.

Генерируется, когда тест или набор тестов выбрасывает ошибку.

Распространение контекста с помощью bindStore()

Канал трассировки можно использовать для распространения контекста во время выполнения тестов, привязав экземпляр AsyncLocalStorage. Это позволяет автоматически сделать контекст доступным в функции теста и во всех асинхронных операциях внутри теста.

import dc from 'node:diagnostics_channel';
import { AsyncLocalStorage } from 'node:async_hooks';

const testStorage = new AsyncLocalStorage();
const testChannel = dc.tracingChannel('node.test');

// Bind context to test execution — the returned value becomes the store
testChannel.start.bindStore(testStorage, (data) => {
  return { testName: data.name, startTime: Date.now() };
});

// Optionally handle errors and cleanup
testChannel.error.subscribe((data) => {
  const store = testStorage.getStore();
  console.log(`Test "${data.name}" failed after ${Date.now() - store.startTime}ms`);
});

testChannel.end.subscribe((data) => {
  const store = testStorage.getStore();
  console.log(`Test "${data.name}" completed in ${Date.now() - store.startTime}ms`);
});
mjscopy

При использовании bindStore() предоставленный контекст будет автоматически распространяться на функцию теста и все асинхронные операции внутри теста без необходимости дополнительного инструментирования кода теста.

Класс: TestContext

Добавлено в: v18.0.0, v16.17.0
История
Версия Изменения
v20.1.0, v18.17.0

Функция before добавлена в TestContext.

Экземпляр TestContext передаётся каждой функции теста для взаимодействия со средством запуска тестов. Однако конструктор TestContext не предоставляется в составе API.

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

Добавлено в: v20.1.0, v18.17.0
  • fn <Function> | <AsyncFunction> Функция хука. Первый аргумент этой функции — объект TestContext. Если хук использует обратные вызовы, функция обратного вызова передаётся вторым аргументом. По умолчанию: пустая функция.
  • options <Object> Параметры конфигурации хука. Поддерживаются следующие свойства:
    • signal <AbortSignal> Позволяет прервать выполняющийся хук.
    • timeout <number> Количество миллисекунд, по истечении которого хук завершится с ошибкой. Если значение не указано, подтесты наследуют его от родительского теста. По умолчанию: Infinity.

Эта функция используется для создания хука, выполняемого перед подтестом текущего теста.

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

Добавлено в: v18.8.0, v16.18.0
  • 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) => {
      // Some relevant assertion here
    },
  );
});
jscopy

context.after([fn][, options])

Добавлено в: v19.3.0, v18.13.0
  • 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}`));
  // Some relevant assertion here
});
jscopy

context.afterEach([fn][, options])

Добавлено в: v18.8.0, v16.18.0
  • 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) => {
      // Some relevant assertion here
    },
  );
});
jscopy

context.assert

Добавлено в: v22.2.0, v20.15.0

Объект, содержащий методы проверки, привязанные к context. Функции верхнего уровня из модуля node:assert доступны здесь для создания планов тестирования.

test('test', (t) => {
  t.plan(1);
  t.assert.strictEqual(true, true);
});
jscopy
context.assert.fileSnapshot(value, path[, options])
Добавлено в: v23.7.0, v22.14.0
  • value <any> Значение для сериализации в строку. Если Node.js запущен с флагом --test-update-snapshots, сериализованное значение записывается в path. В противном случае сериализованное значение сравнивается с содержимым существующего файла снимка.
  • path <string> Файл, в который записывается сериализованное value.
  • options <Object> Необязательные параметры конфигурации. Поддерживаются следующие свойства:
    • serializers <Array> Массив синхронных функций, используемых для сериализации value в строку. value передаётся как единственный аргумент первой функции сериализатора. Возвращаемое значение каждого сериализатора передаётся в качестве входных данных следующему сериализатору. После выполнения всех сериализаторов результирующее значение преобразуется в строку. По умолчанию: если сериализаторы не указаны, используются сериализаторы тестового средства по умолчанию.

Эта функция сериализует value и записывает его в файл, указанный в path.

test('snapshot test with default serialization', (t) => {
  t.assert.fileSnapshot({ value1: 1, value2: 2 }, './snapshots/snapshot.json');
});
jscopy

Эта функция отличается от context.assert.snapshot() следующим:

  • Путь к файлу снимка явно указывается пользователем.
  • Каждый файл снимка содержит только одно значение снимка.
  • Тестовое средство не выполняет дополнительное экранирование.

Эти различия позволяют файлам снимков лучше поддерживать такие возможности, как подсветка синтаксиса.

context.assert.snapshot(value[, options])
Добавлено в: v22.3.0
  • value <any> Значение для сериализации в строку. Если Node.js запущен с флагом --test-update-snapshots, сериализованное значение записывается в файл снимка. В противном случае сериализованное значение сравнивается с соответствующим значением в существующем файле снимка.
  • options <Object> Необязательные параметры конфигурации. Поддерживаются следующие свойства:
    • serializers <Array> Массив синхронных функций, используемых для сериализации value в строку. value передаётся как единственный аргумент первой функции сериализатора. Возвращаемое значение каждого сериализатора передаётся в качестве входных данных следующему сериализатору. После выполнения всех сериализаторов результирующее значение преобразуется в строку. По умолчанию: если сериализаторы не указаны, используются сериализаторы тестового средства по умолчанию.

Эта функция реализует утверждения для тестирования снимков.

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)],
  });
});
jscopy

context.diagnostic(message)

Добавлено в: v18.0.0, v16.17.0
  • message <string> Сообщение для вывода.

Эта функция используется для вывода диагностических сообщений. Любая диагностическая информация включается в конец результатов теста. Эта функция не возвращает значение.

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

context.filePath

Добавлено в: v22.6.0, v20.16.0

Абсолютный путь к файлу теста, создавшему текущий тест. Если файл теста импортирует дополнительные модули, создающие тесты, импортированные тесты будут возвращать путь к корневому файлу теста.

context.fullName

Добавлено в: v22.3.0, v20.16.0

Имя теста и имена всех его предков, разделённые >.

context.name

Добавлено в: v18.8.0, v16.18.0

Имя теста.

context.passed

Добавлено в: v21.7.0, v20.12.0
  • Тип: <boolean> false до выполнения теста, например в хуке beforeEach.

Указывает, успешно ли завершился тест.

context.error

Добавлено в: v21.7.0, v20.12.0
  • Тип: <Error> | <null>

Причина сбоя теста/сценария; обёрнута и доступна через context.error.cause.

context.attempt

Добавлено в: v25.0.0
  • Тип: <number>

Номер попытки выполнения теста. Значение отсчитывается с нуля: первая попытка — 0, вторая — 1 и так далее. Это свойство полезно использовать вместе с параметром --test-rerun-failures, чтобы определить, какая попытка теста выполняется в данный момент.

context.tags

Добавлено в: v26.2.0

Стабильность: 1.0 — ранняя стадия разработки

  • Тип: <string[]>

Замороженный массив тегов теста в нижнем регистре, сведённых к плоскому списку в порядке объявления, включая теги, унаследованные от родительских наборов тестов. Пуст, если у теста нет тегов. См. Теги тестов.

context.workerId

Добавлено в: v25.8.0
  • Тип: <number> | <undefined>

Уникальный идентификатор рабочего процесса, выполняющего текущий файл теста. Это значение определяется переменной окружения NODE_TEST_WORKER_ID. При запуске тестов с --test-isolation=process (значение по умолчанию) каждый файл теста выполняется в отдельном дочернем процессе и получает идентификатор рабочего процесса от 1 до N, где N — число параллельно работающих рабочих процессов. При запуске с --test-isolation=none все тесты выполняются в одном процессе, а идентификатор рабочего процесса всегда равен 1. Если выполнение происходит вне контекста теста, значение равно undefined.

Это свойство полезно для распределения ресурсов (например, подключений к базам данных или портов серверов) между одновременно выполняющимися файлами тестов:

import { test } from 'node:test';
import { process } from 'node:process';

test('database operations', async (t) => {
  // Worker ID is available via context
  console.log(`Running in worker ${t.workerId}`);

  // Or via environment variable (available at import time)
  const workerId = process.env.NODE_TEST_WORKER_ID;
  // Use workerId to allocate separate resources per worker
});
mjscopy

context.plan(count[,options])

Добавлено в: v22.2.0, v20.15.0
История
Версия Изменения
v23.9.0, v22.15.0

Добавлен параметр options.

v23.4.0, v22.13.0

Эта функция больше не является экспериментальной.

  • count <number> Количество утверждений и подзадач, выполнение которых ожидается.
  • options <Object> Дополнительные параметры плана. Поддерживаются следующие свойства:
    • wait <boolean> | <number> Время ожидания плана:
      • Если true, план бесконечно ждёт выполнения всех утверждений и подзадач.
      • Если false, план сразу выполняет проверку после завершения функции теста, не дожидаясь выполнения ожидающих утверждений или подзадач. Утверждения и подзадачи, завершившиеся после этой проверки, не будут учитываться в плане.
      • Если указано число, оно задаёт максимальное время ожидания в миллисекундах до истечения времени ожидания при проверке соответствия ожидаемым утверждениям и подзадачам. По истечении этого времени тест завершится с ошибкой. По умолчанию: false.

Эта функция задаёт количество утверждений и подзадач, выполнение которых ожидается в рамках теста. Если фактическое количество выполненных утверждений и подзадач не совпадает с ожидаемым, тест завершится с ошибкой.

Примечание: чтобы отслеживать утверждения, необходимо использовать t.assert вместо прямого вызова assert.

test('top level test', (t) => {
  t.plan(2);
  t.assert.ok('some relevant assertion here');
  t.test('subtest', () => {});
});
jscopy

При работе с асинхронным кодом для проверки выполнения правильного количества утверждений можно использовать функцию plan:

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

При использовании параметра wait можно управлять временем ожидания выполнения ожидаемых утверждений тестом. Например, указание максимального времени ожидания гарантирует, что тест будет ждать завершения асинхронных утверждений в течение заданного периода:

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

Примечание: если задано время ожидания wait, его отсчёт начинается только после завершения выполнения функции теста.

context.runOnly(shouldRunOnlyTests)

Добавлено в: v18.0.0, v16.17.0
  • 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 }),
  ]);
});
jscopy

context.signal

Добавлено в: v18.7.0, v16.17.0
  • Тип: <AbortSignal>

Можно использовать для отмены подзадач теста после отмены самого теста.

test('top level test', async (t) => {
  await fetch('some/uri', { signal: t.signal });
});
jscopy

context.skip([message])

Добавлено в: v18.0.0, v16.17.0
  • 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');
});
jscopy

context.todo([message])

Добавлено в: v18.0.0, v16.17.0
  • message <string> Необязательное сообщение TODO.

Эта функция добавляет директиву TODO в вывод теста. Если указано message, оно включается в вывод. Вызов todo() не прекращает выполнение функции теста. Эта функция не возвращает значение.

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

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

Добавлено в: v18.0.0, v16.17.0
История
Версия Изменения
v26.2.0

Добавлен параметр tags.

v18.8.0, v16.18.0

Добавлен параметр signal.

v18.7.0, v16.17.0

Добавлен параметр timeout.

  • name <string> Имя подтеста, отображаемое при выводе результатов тестирования. По умолчанию: свойство name объекта fn или '<anonymous>', если у fn нет имени.
  • options <Object> Параметры конфигурации подтеста. Поддерживаются следующие свойства:
    • concurrency <number> | <boolean> | <null> Если указано число, такое количество тестов будет выполняться асинхронно (при этом ими по-прежнему управляет однопоточный цикл событий). Если указано true, все подтесты будут выполняться параллельно. Если указано false, одновременно будет выполняться только один тест. Если значение не указано, подтесты наследуют его от родительского теста. По умолчанию: null.
    • only <boolean> Если значение истинно и контекст теста настроен на запуск тестов only, этот тест будет запущен. В противном случае тест будет пропущен. По умолчанию: false.
    • signal <AbortSignal> Позволяет отменить выполняющийся тест.
    • skip <boolean> | <string> Если значение истинно, тест пропускается. Если указана строка, она отображается в результатах тестирования как причина пропуска теста. По умолчанию: false.
    • tags <string[]> Массив строковых меток, связанных с подтестом. Используется вместе с --experimental-test-tag-filter для фильтрации запускаемых тестов. Теги объединяются с тегами родительского теста или набора тестов. См. Теги тестов. По умолчанию: [].
    • todo <boolean> | <string> Если значение истинно, тест помечается как TODO. Если указана строка, она отображается в результатах тестирования как причина, по которой тест TODO. По умолчанию: false.
    • timeout <number> Количество миллисекунд, по истечении которых тест завершится с ошибкой. Если значение не указано, подтесты наследуют его от родительского теста. По умолчанию: Infinity.
    • plan <number> Ожидаемое количество утверждений и подтестов, которые должны быть выполнены в тесте. Если фактическое количество выполненных утверждений не совпадает с количеством, указанным в плане, тест завершится с ошибкой. По умолчанию: undefined.
  • 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, plan: 1 },
    (t) => {
      t.assert.ok('some relevant assertion here');
    },
  );
});
jscopy

context.waitFor(condition[, options])

Добавлено в: v23.7.0, v22.14.0
  • condition <Function> | <AsyncFunction> Функция утверждения, периодически вызываемая до успешного завершения или истечения заданного времени ожидания опроса. Успешным считается завершение без выброса исключения или отклонения промиса. Эта функция не принимает аргументов и может возвращать любое значение.
  • options <Object> Необязательный объект конфигурации для операции опроса. Поддерживаются следующие свойства:
    • interval <number> Количество миллисекунд ожидания после неудачного вызова condition перед следующей попыткой. По умолчанию: 50.
    • timeout <number> Время ожидания опроса в миллисекундах. Если condition не завершится успешно до истечения этого времени, возникнет ошибка. По умолчанию: 1000.
  • Возвращает: <Promise> Выполняется со значением, возвращённым condition.

Этот метод опрашивает функцию condition до тех пор, пока она не завершится успешно или не истечёт время ожидания операции.

Класс: SuiteContext

Добавлено в: v18.7.0, v16.17.0

Экземпляр SuiteContext передаётся каждой функции набора тестов для взаимодействия с тестовым средством. Однако конструктор SuiteContext не предоставляется в составе API.

context.filePath

Добавлено в: v22.6.0

Абсолютный путь к файлу теста, создавшему текущий набор тестов. Если файл теста импортирует дополнительные модули, создающие наборы тестов, импортированные наборы будут возвращать путь к корневому файлу теста.

context.fullName

Добавлено в: v22.3.0, v20.16.0

Имя набора тестов и имена всех его предков, разделённые >.

context.name

Добавлено в: v18.8.0, v16.18.0

Имя набора тестов.

context.signal

Добавлено в: v18.7.0, v16.17.0
  • Тип: <AbortSignal>

Можно использовать для отмены подзадач теста после отмены самого теста.

context.passed

Добавлено в: v26.1.0
  • Тип: <boolean>

Указывает, успешно ли завершились набор тестов и все его подтесты.

context.attempt

Добавлено в: v26.1.0
  • Тип: <number>

Номер попытки выполнения набора тестов. Значение отсчитывается с нуля: первая попытка — 0, вторая — 1 и так далее. Это свойство полезно использовать вместе с параметром --test-rerun-failures, чтобы определить номер текущей попытки.

context.diagnostic(message)

Добавлено в: v26.1.0
  • message <string> Диагностическое сообщение для вывода.

Выводит диагностическое сообщение. Обычно используется для записи сведений о текущем наборе тестов или его тестах.

test.describe('my suite', (suite) => {
  suite.diagnostic('Suite diagnostic message');
});
jscopy

© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/api/test.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API