Запуск тестов
Исходный код: lib/test.js
Модуль node:test облегчает создание тестов JavaScript. Для доступа к нему:
Модули MJS
import test from 'node:test';
Модули CJS
const test = require('node:test'); Этот модуль доступен только по схеме node:. Следующее не сработает:
Модули MJS
import test from 'test';
Модули CJS
const test = require('test'); Тесты, созданные с помощью модуля test, состоят из одной функции, которая обрабатывается одним из трех способов:
- Синхронная функция, которая считается неудачной, если выбрасывает исключение, и успешной в противном случае.
- Функция, которая возвращает
Promise, которая считается неудачной, еслиPromiseотклоняется, и успешной, еслиPromiseвыполняется. - Функция, которая получает функцию обратного вызова. Если функция обратного вызова получает любое истинное значение в качестве своего первого аргумента, тест считается неудачным. Если в качестве первого аргумента функции обратного вызова передается ложное значение, тест считается успешным. Если функция теста получает функцию обратного вызова и также возвращает
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'));
});
}); copy Если какие-либо тесты завершаются неудачей, код завершения процесса устанавливается в 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);
});
}); copy Примечание: хуки
beforeEachиafterEachсрабатывают между каждым выполнением подтеста.
В этом примере используется await, чтобы гарантировать завершение обоих подтестов. Это необходимо, потому что тесты не ожидают завершения своих подтестов, в отличие от тестов, созданных в рамках наборов. Любые подтесты, которые все еще активны, когда заканчивается их родительский тест, отменяются и считаются неудачными. Любая неудача подтеста приводит к неудаче родительского теста.
Пропуск тестов
Отдельные тесты могут быть пропущены, передав опцию skip тесту или вызвав метод skip() контекста теста, как показано в следующем примере.
// The skip option is used, but no message is provided.
test('skip option', { skip: true }, (t) => {
// This code is never executed.
});
// The skip option is used, and a message is provided.
test('skip option with message', { skip: 'this is skipped' }, (t) => {
// This code is never executed.
});
test('skip() method', (t) => {
// Make sure to return here as well if the test contains additional logic.
t.skip();
});
test('skip() method with message', (t) => {
// Make sure to return here as well if the test contains additional logic.
t.skip('this is skipped');
}); copy Тесты 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');
}); copy
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);
});
});
}); copy describe() и it() импортируются из модуля node:test.
Модули MJS
import { describe, it } from 'node:test';
Модули CJS
const { describe, it } = require('node:test');
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.
});
}); copy Фильтрация тестов по имени
Параметр командной строки --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');
}); copy Шаблоны имён тестов также можно задавать с использованием литералов регулярных выражений. Это позволяет использовать флаги регулярных выражений. В предыдущем примере запуск 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');
}); copy Запуск Node.js с --test-name-pattern="test 1 some test" совпадёт только с some test в test 1.
Шаблоны имён тестов не изменяют набор файлов, которые выполняет запускатель тестов.
Если и --test-name-pattern и --test-skip-pattern указаны, тесты должны удовлетворять обеим требованиям, чтобы выполняться.
Внезапная асинхронная активность
После завершения выполнения функции теста результаты сообщаются как можно быстрее, сохраняя порядок тестов. Однако возможно, что функция теста генерирует асинхронную активность, которая существует дольше, чем сам тест. Запускатель тестов обрабатывает этот тип активности, но не задерживает отчет о результатах тестов для его учета.
В следующем примере тест завершается, а две операции setImmediate() всё ещё активны. Первый setImmediate() пытается создать новый подтест. Так как родительский тест уже завершился и вывел свои результаты, новый подтест сразу же помечается как неудачный и сообщается позже в <Поток тестов>.
Второй setImmediate() создаёт событие uncaughtException. События uncaughtException и unhandledRejection, исходящие из завершённого теста, помечаются как неудачные модулем test и сообщаются как диагностические предупреждения на верхнем уровне потоком <Поток тестов>.
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.
}); copy Режим наблюдения
Запускатель тестов Node.js поддерживает режим наблюдения, передавая флаг --watch:
node --test --watch copy
В режиме наблюдения запускатель тестов будет следить за изменениями файлов тестов и их зависимостей. При обнаружении изменения запускатель тестов повторно выполнит тесты, затронутые изменением. Запускатель тестов будет продолжать работу до завершения процесса.
Запуск тестов из командной строки
Запуск исполнителя тестов Node.js из командной строки можно осуществить, передав флаг --test:
node --test copy
По умолчанию Node.js будет запускать все файлы, соответствующие этим шаблонам:
**/*.test.?(c|m)js**/*-test.?(c|m)js**/*_test.?(c|m)js**/test-*.?(c|m)js**/test.?(c|m)js**/test/**/*.?(c|m)js
В качестве альтернативы можно передать один или несколько шаблонов glob в качестве последних аргументов командной строки Node.js, как показано ниже. Шаблоны glob следуют поведению glob(7). Шаблоны glob должны быть заключены в двойные кавычки в командной строке, чтобы предотвратить расширение оболочки, что может снизить переносимость между системами.
node --test "**/*.test.js" "**/*.spec.js" copy
Соответствующие файлы выполняются как файлы тестов. Более подробную информацию о выполнении файлов тестов можно найти в разделе модель выполнения исполнителя тестов.
Модель выполнения исполнителя тестов
Каждый соответствующий файл теста выполняется в отдельном дочернем процессе. Максимальное количество дочерних процессов, работающих одновременно, контролируется флагом --test-concurrency. Если дочерний процесс завершается с кодом выхода 0, тест считается пройденным. В противном случае тест считается неудачным. Файлы тестов должны быть исполняемыми для Node.js, но не обязаны использовать модуль node:test во внутренней работе.
Каждый файл теста выполняется так, как если бы это был обычный скрипт. То есть, если сам файл теста использует node:test для определения тестов, все эти тесты будут выполняться в одном потоке приложения, независимо от значения опции concurrency объекта test().
Сбор покрытия кода
Когда Node.js запускается с флагом командной строки --experimental-test-coverage, собирается покрытие кода, и статистика отображается после завершения всех тестов. Если переменная окружения NODE_V8_COVERAGE используется для указания каталога покрытия кода, сгенерированные файлы покрытия V8 записываются в этот каталог. Модули ядра Node.js и файлы внутри каталогов node_modules/ не включаются в отчет о покрытии. Если покрытие включено, отчет о покрытии отправляется любым отчетчикам тестов через событие '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 */ copy Покрытие кода также можно отключить для определённого числа строк. После указанного количества строк покрытие будет автоматически включено. Если количество строк не указано явно, игнорируется одна строка.
/* 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');
} copy Отчетчики покрытия
Отчетчики tap и spec будут выводить сводку статистики покрытия. Также есть отчетчик lcov, который сгенерирует файл lcov, который можно использовать для подробного отчёта о покрытии.
node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=lcov.info copy
Ограничения
Функциональность покрытия кода исполнителя тестов не поддерживает исключение определённых файлов или каталогов из отчёта о покрытии.
Мокирование
Модуль node:test поддерживает мокирование во время тестирования через глобальный объект mock. Следующий пример создаёт шпион (spy) для функции, складывающей два числа. Затем шпион используется для проверки того, что функция была вызвана ожидаемым образом.
Модули MJS
import assert from 'node:assert';
import { mock, test } from 'node:test';
test('spies on a function', () => {
const sum = mock.fn((a, b) => {
return a + b;
});
assert.strictEqual(sum.mock.calls.length, 0);
assert.strictEqual(sum(3, 4), 7);
assert.strictEqual(sum.mock.calls.length, 1);
const call = sum.mock.calls[0];
assert.deepStrictEqual(call.arguments, [3, 4]);
assert.strictEqual(call.result, 7);
assert.strictEqual(call.error, undefined);
// Reset the globally tracked mocks.
mock.reset();
});
Модули CJS
'use strict';
const assert = require('node:assert');
const { mock, test } = require('node:test');
test('spies on a function', () => {
const sum = mock.fn((a, b) => {
return a + b;
});
assert.strictEqual(sum.mock.calls.length, 0);
assert.strictEqual(sum(3, 4), 7);
assert.strictEqual(sum.mock.calls.length, 1);
const call = sum.mock.calls[0];
assert.deepStrictEqual(call.arguments, [3, 4]);
assert.strictEqual(call.result, 7);
assert.strictEqual(call.error, undefined);
// Reset the globally tracked mocks.
mock.reset();
}); Та же функциональность мокирования также доступна в объекте TestContext каждого теста. В следующем примере создаётся шпион для метода объекта, используя 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.calls.length, 0);
assert.strictEqual(number.add(3), 8);
assert.strictEqual(number.add.mock.calls.length, 1);
const call = number.add.mock.calls[0];
assert.deepStrictEqual(call.arguments, [3]);
assert.strictEqual(call.result, 8);
assert.strictEqual(call.target, undefined);
assert.strictEqual(call.this, number);
}); copy Таймеры
Мокирование таймеров — это техника, часто используемая при тестировании ПО, для моделирования и контроля поведения таймеров, таких как setInterval и setTimeout, без реального ожидания указанных интервалов времени.
Обратитесь к классу MockTimers для полного списка методов и функций.
Это позволяет разработчикам писать более надёжные и предсказуемые тесты для функциональности, зависящей от времени.
Пример ниже демонстрирует как смокировать setTimeout. Используя .enable({ apis: ['setTimeout'] }); это смокирует функции setTimeout в модулях node:timers и node:timers/promises, а также из глобального контекста Node.js.
Примечание: Деструктуризация функций, таких как import { setTimeout } from 'node:timers', в данный момент не поддерживается данным API.
Модули MJS
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();
});
Модули CJS
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 каждого теста. Преимущество мокирования через контекст теста заключается в том, что исполнитель тестов автоматически восстановит всю смокированную функциональность таймеров после завершения теста.
Модули MJS
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);
});
Модули CJS
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().
Модули MJS
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);
});
Модули CJS
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);
}); Если начальная эпоха не задана, начальная дата будет основана на 0 в Unix-эпохе. Это 1 января 1970 года, 00:00:00 UTC. Вы можете задать начальную дату, передав свойство now методу .enable(). Это значение будет использовано в качестве начальной даты для смокированного объекта Date. Это может быть положительное целое число или другой объект Date.
Модули MJS
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);
});
Модули CJS
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() для ручного перемещения смокированной даты в другое время. Этот метод принимает только положительное целое число.
Примечание: Этот метод выполнит все смокированные таймеры, которые находятся в прошлом от нового времени.
В примере ниже мы устанавливаем новое время для смокированной даты.
Модули MJS
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);
});
Модули CJS
const assert = require('node:assert');
const { test } = require('node:test');
test('sets the time of a date object', (context) => {
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['Date'], now: 100 });
assert.strictEqual(Date.now(), 100);
// Advance in time will also advance the date
context.mock.timers.setTime(1000);
context.mock.timers.tick(200);
assert.strictEqual(Date.now(), 1200);
}); Если у вас есть таймер, установленный для запуска в прошлом, он будет выполнен так, как если бы был вызван метод .tick(). Это полезно, если вы хотите протестировать функциональность, зависящую от времени, которая уже в прошлом.
Модули MJS
import assert from 'node:assert';
import { test } from 'node:test';
test('runs timers as setTime passes ticks', (context) => {
// Optionally choose what to mock
context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
const fn = context.mock.fn();
setTimeout(fn, 1000);
context.mock.timers.setTime(800);
// Timer is not executed as the time is not yet reached
assert.strictEqual(fn.mock.callCount(), 0);
assert.strictEqual(Date.now(), 800);
context.mock.timers.setTime(1200);
// Timer is executed as the time is now reached
assert.strictEqual(fn.mock.callCount(), 1);
assert.strictEqual(Date.now(), 1200);
});
Модули CJS
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() выполнит все таймеры, которые находятся в очереди. Это также продвинет смокированную дату к времени последнего выполненного таймера, как будто время прошло.
Модули MJS
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);
});
Модули CJS
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);
}); Тестирование снимков
Тесты снимков позволяют произвольным значениям сериализоваться в строковые значения и сравниваться с набором известных хороших значений. Известные хорошие значения называются снимками и хранятся в файле снимка. Файлы снимков управляются исполнителем тестов, но предназначены для удобства чтения человеком для отладки. Лучшая практика — включать файлы снимков в систему управления версиями вместе с файлами тестов. Для активации тестирования снимков Node.js должен быть запущен с флагом командной строки --experimental-test-snapshots.
Файлы снимков создаются путём запуска Node.js с флагом командной строки --test-update-snapshots. Для каждого файла теста генерируется отдельный файл снимка. По умолчанию файл снимка имеет то же имя, что и process.argv[1], с расширением .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);
});
}); copy Сгенерируйте файл снимка, запустив файл теста с флагом --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
`; copy После создания файла снимка запустите тесты снова без флага --test-update-snapshots. Теперь тесты должны пройти успешно.
Отчётчики тестов
Модуль node:test поддерживает передачу флагов --test-reporter для запуска тестов с использованием определённого отчётчика.
Поддерживаются следующие встроенные отчётчики:
-
tapОтчётчикtapвыводит результаты тестов в формате TAP. -
specОтчётчикspecвыводит результаты тестов в удобочитаемом формате. -
dotОтчётчикdotвыводит результаты тестов в компактном формате, где каждый пройденный тест представлен., а каждый неудачный тест —X. -
junitОтчётчик junit выводит результаты тестов в формате XML jUnit. -
lcovОтчётчикlcovвыводит покрытие кода при использовании флага--experimental-test-coverage.
Если stdout является TTY, то по умолчанию используется отчётчик spec . В противном случае по умолчанию используется отчётчик tap.
Точный вывод этих отчётчиков может изменяться между версиями Node.js и не должен использоваться в программах. Если требуется программно получить вывод запуска тестов, используйте события, испускаемые <TestsStream>.
Отчётчики доступны через модуль node:test/reporters:
Модули MJS
import { tap, spec, dot, junit, lcov } from 'node:test/reporters';
Модули CJS
const { tap, spec, dot, junit, lcov } = require('node:test/reporters'); Настраиваемые отчётчики
--test-reporter можно использовать для указания пути к настраиваемому отчётчику. Настраиваемый отчётчик — это модуль, который экспортирует значение, принимаемое stream.compose. Отчётчики должны преобразовывать события, испускаемые <TestsStream>.
Пример настраиваемого отчётчика с использованием <stream.Transform>:
Модули MJS
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: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;
Модули CJS
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: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; Пример настраиваемого отчётчика с использованием генераторной функции:
Модули MJS
export default async function * customReporter(source) {
for await (const event of source) {
switch (event.type) {
case 'test:dequeue':
yield `test ${event.data.name} dequeued`;
break;
case 'test:enqueue':
yield `test ${event.data.name} enqueued`;
break;
case 'test:watch:drained':
yield 'test watch queue drained';
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';
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;
}
}
}
}
Модули CJS
module.exports = async function * customReporter(source) {
for await (const event of source) {
switch (event.type) {
case 'test:dequeue':
yield `test ${event.data.name} dequeued`;
break;
case 'test:enqueue':
yield `test ${event.data.name} enqueued`;
break;
case 'test:watch:drained':
yield 'test watch queue drained';
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 copy
Когда указан единственный отчётчик, назначение по умолчанию будет stdout, если явно не указано другое.
run([options])
-
options<Объект> Параметры конфигурации для запуска тестов. Поддерживаются следующие свойства:-
concurrency<число> | <логическое значение> Если задано число, то столько процессов будет запущено параллельно для каждого файла тестов. Еслиtrue, тоos.availableParallelism() - 1файлы тестов будут запущены параллельно. Еслиfalse, то будет запущен только один файл тестов за раз. По умолчанию:false. -
files: <Массив> Массив, содержащий список файлов для запуска. По умолчанию соответствуют файлам из модели исполнения запуска тестов. -
forceExit: <логическое значение> Настраивает запуск тестов на выход из процесса после завершения всех известных тестов, даже если цикл событий остаётся активным. По умолчанию:false. -
inspectPort<число> | <Функция> Устанавливает порт инспектора для дочернего процесса тестирования. Это может быть число или функция, которая не принимает аргументов и возвращает число. Если задано нулевое значение, каждый процесс получает свой порт, инкрементированный от порта основного процессаprocess.debugPort. По умолчанию:undefined. -
only: <логическое значение> Если истинно, контекст тестирования будет запускать только тесты, для которых установлен параметрonly. -
setup<Функция> Функция, которая принимает экземплярTestsStreamи может использоваться для настройки слушателей до запуска любых тестов. По умолчанию:undefined. -
signal<AbortSignal> Разрешает прерывание процесса выполнения тестов. -
testNamePatterns<строка> | <RegExp> | <Массив> Строка, RegExp или массив RegExp, который можно использовать для запуска только тестов, чьё имя соответствует указанному шаблону. Шаблоны имён тестов интерпретируются как JavaScript регулярные выражения. Для каждого исполняемого теста также выполняются соответствующие хуки тестов, например,beforeEach(). По умолчанию:undefined. -
timeout<число> Количество миллисекунд, по истечении которых выполнение теста завершится с ошибкой. Если не указано, подтесты наследуют это значение от родительского теста. По умолчанию:Infinity. -
watch<логическое значение> Запускать ли в режиме просмотра. По умолчанию:false. -
shard<Объект> Запуск тестов в определённом фрагменте. По умолчанию:undefined.
-
- Возвращает: <TestsStream>
Примечание: shard используется для горизонтального распараллеливания запуска тестов на различных машинах или процессах, идеально подходит для масштабных выполнений в различных средах. Несовместим с режимом watch , предназначенным для быстрой итерации кода путём автоматического повторного запуска тестов при изменениях файлов.
Модули MJS
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);
Модули CJS
const { tap } = require('node:test/reporters');
const { run } = require('node:test');
const path = require('node:path');
run({ files: [path.resolve('./tests/test.js')] })
.on('test:fail', () => {
process.exitCode = 1;
})
.compose(tap)
.pipe(process.stdout);
suite([name][, options][, fn])
-
name<строка> Название набора тестов, отображаемое при сообщении о результатах. По умолчанию: свойствоnameобъектаfn, или'<anonymous>', если уfnнет имени. -
options<Объект> Необязательные параметры конфигурации набора. Поддерживает те же параметры, что иtest([name][, options][, fn]). -
fn<Функция> | <Асинхронная функция> Функция набора, объявляющая вложенные тесты и наборы. Первым аргументом этой функции является объектSuiteContext. По умолчанию: функция без действий. - Возвращает: <Обещание> Немедленно выполняется с
undefined.
Функция suite() импортирована из модуля node:test.
suite.skip([name][, options][, fn])
Сокращенная запись для пропуска набора. Это то же самое, что и suite([name], { skip: true }[, fn]).
suite.todo([name][, options][, fn])
Сокращенная запись для обозначения набора как TODO. Это то же самое, что и suite([name], { todo: true }[, fn]).
suite.only([name][, options][, fn])
Сокращенная запись для обозначения набора как only. Это то же самое, что и suite([name], { only: true }[, fn]).
test([name][, options][, fn])
-
name<строка> Название теста, отображаемое при сообщении о результатах. По умолчанию: свойствоnameобъектаfn, или'<anonymous>', если уfnнет имени. -
options<Объект> Параметры конфигурации для теста. Поддерживаются следующие свойства:-
concurrency<число> | <логическое значение> Если указано число, столько тестов будет выполняться параллельно в потоке приложения. Еслиtrue, все запланированные асинхронные тесты выполняются одновременно в потоке. Еслиfalse, только один тест выполняется за раз. Если не указано, подтесты наследуют это значение от родительского теста. По умолчанию:false. -
only<логическое значение> Если истинно, и контекст теста настроен на выполнениеonlyтестов, то этот тест будет выполнен. В противном случае тест пропускается. По умолчанию:false. -
signal<AbortSignal> Разрешает прерывание текущего теста. -
skip<логическое значение> | <строка> Если истинно, тест пропускается. Если указана строка, эта строка отображается в результатах теста как причина пропуска. По умолчанию:false. -
todo<логическое значение> | <строка> Если истинно, тест помечен какTODO. Если указана строка, эта строка отображается в результатах теста как причина, по которой тестTODO. По умолчанию:false. -
timeout<число> Количество миллисекунд, через которое тест будет считаться проваленным. Если не указано, подтесты наследуют это значение от родительского теста. По умолчанию:Infinity. -
plan<число> Количество ожидаемых утверждений и подтестов, которые будут выполнены в тесте. Если количество выполненных утверждений в тесте не соответствует указанному в плане, тест будет считаться проваленным. По умолчанию:undefined.
-
-
fn<Функция> | <Асинхронная функция> Функция, подлежащая тестированию. Первым аргументом этой функции является объектTestContext. Если тест использует обратные вызовы, функция обратного вызова передаётся в качестве второго аргумента. По умолчанию: функция без действий. - Возвращает: <Обещание> Выполняется, когда тест завершается, или немедленно, если тест выполняется внутри набора.
Функция test() - это значение, импортированное из модуля test. Каждый вызов этой функции приводит к сообщению о тесте в <ПотокТестов>.
Объект TestContext, переданный в аргумент fn, может быть использован для выполнения действий, связанных с текущим тестом. Примеры включают пропуск теста, добавление дополнительной диагностической информации или создание подтестов.
test() возвращает Promise, который выполняется, когда тест завершается. Если test() вызывается внутри набора, он выполняется немедленно. Значение возврата обычно можно игнорировать для тестов верхнего уровня. Однако значение возврата из подтестов необходимо использовать, чтобы предотвратить завершение родительского теста раньше и отмену подтеста, как показано в следующем примере.
test('top level test', async (t) => {
// The setTimeout() in the following subtest would cause it to outlive its
// parent test if 'await' is removed on the next line. Once the parent test
// completes, it will cancel any outstanding subtests.
await t.test('longer running subtest', async (t) => {
return new Promise((resolve, reject) => {
setTimeout(resolve, 1000);
});
});
}); copy Параметр 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])
Сокращение для обозначения набора как only. Это то же самое, что и describe([name], { only: true }[, fn]).
it([name][, options][, fn])
Псевдоним для test().
Функция it() импортирована из модуля node:test.
it.skip([name][, options][, fn])
Сокращение для пропуска теста, то же, что и it([name], { skip: true }[, fn]).
it.todo([name][, options][, fn])
Сокращение для обозначения теста как TODO, то же, что и it([name], { todo: true }[, fn]).
it.only([name][, options][, fn])
Сокращенная запись для маркировки теста как only, аналогично it([name], { only: true }[, fn]).
before([fn][, options])
-
fn<Функция> | <Асинхронная функция> Функция-обработчик. Если обработчик использует колбэки, функция колбэка передается в качестве второго аргумента. По умолчанию: функция-пустышка. -
options<Объект> Параметры конфигурации для обработчика. Поддерживаются следующие свойства:-
signal<AbortSignal> Разрешает прерывание выполнения обработчика. -
timeout<число> Количество миллисекунд, после которого обработчик завершится с ошибкой. Если не указано, подтесты наследуют это значение от родительского теста. По умолчанию:Infinity.
-
Эта функция создает обработчик, который выполняется перед выполнением набора тестов.
describe('tests', async () => {
before(() => console.log('about to run some test'));
it('is a subtest', () => {
assert.ok('some relevant assertion here');
});
}); copy
after([fn][, options])
-
fn<Функция> | <Асинхронная функция> Функция-обработчик. Если обработчик использует колбэки, функция колбэка передается в качестве второго аргумента. По умолчанию: функция-пустышка. -
options<Объект> Параметры конфигурации для обработчика. Поддерживаются следующие свойства:-
signal<AbortSignal> Разрешает прерывание выполнения обработчика. -
timeout<число> Количество миллисекунд, после которого обработчик завершится с ошибкой. Если не указано, подтесты наследуют это значение от родительского теста. По умолчанию:Infinity.
-
Эта функция создает обработчик, который выполняется после выполнения набора тестов.
describe('tests', async () => {
after(() => console.log('finished running tests'));
it('is a subtest', () => {
assert.ok('some relevant assertion here');
});
}); copy Примечание: Обработчик after гарантированно выполняется, даже если тесты в наборе завершаются с ошибкой.
beforeEach([fn][, options])
-
fn<Функция> | <Асинхронная функция> Функция-обработчик. Если обработчик использует колбэки, функция колбэка передается в качестве второго аргумента. По умолчанию: функция-пустышка. -
options<Объект> Параметры конфигурации для обработчика. Поддерживаются следующие свойства:-
signal<AbortSignal> Разрешает прерывание выполнения обработчика. -
timeout<число> Количество миллисекунд, после которого обработчик завершится с ошибкой. Если не указано, подтесты наследуют это значение от родительского теста. По умолчанию:Infinity.
-
Эта функция создаёт обработчик, который выполняется перед каждым тестом в текущем наборе.
describe('tests', async () => {
beforeEach(() => console.log('about to run a test'));
it('is a subtest', () => {
assert.ok('some relevant assertion here');
});
}); copy
afterEach([fn][, options])
-
fn<Функция> | <Асинхронная функция> Функция-обработчик. Если обработчик использует колбэки, функция колбэка передается в качестве второго аргумента. По умолчанию: функция-пустышка. -
options<Объект> Параметры конфигурации для обработчика. Поддерживаются следующие свойства:-
signal<AbortSignal> Разрешает прерывание выполнения обработчика. -
timeout<число> Количество миллисекунд, после которого обработчик завершится с ошибкой. Если не указано, подтесты наследуют это значение от родительского теста. По умолчанию:Infinity.
-
Эта функция создаёт обработчик, который выполняется после каждого теста в текущем наборе. Обработчик afterEach() выполняется даже если тест завершается с ошибкой.
describe('tests', async () => {
afterEach(() => console.log('finished running a test'));
it('is a subtest', () => {
assert.ok('some relevant assertion here');
});
}); copy
snapshot
Объект, методы которого используются для настройки параметров снимков по умолчанию в текущем процессе. Можно применить ту же конфигурацию ко всем файлам, поместив код конфигурации в модуль, предварительно загруженный с помощью --require или --import.
snapshot.setDefaultSnapshotSerializers(serializers)
-
serializers<Массив> Массив синхронных функций, используемых в качестве стандартных сериализаторов для тестов с сохранением снимков.
Эта функция используется для настройки механизма сериализации по умолчанию, используемого исполнителем тестов. По умолчанию, исполнитель тестов выполняет сериализацию, вызывая JSON.stringify(value, null, 2) для предоставленного значения. JSON.stringify() имеет ограничения относительно циклических структур и поддерживаемых типов данных. Если требуется более надёжный механизм сериализации, следует использовать эту функцию.
snapshot.setResolveSnapshotPath(fn)
-
fn<Функция> Функция, используемая для вычисления расположения файла снимка. Функция получает в качестве единственного аргумента путь к файлу теста. Еслиprocess.argv[1]не связан с файлом (например, в REPL), входные данные undefined.fn()должна вернуть строку, указывающую расположение файла снимка.
Эта функция используется для настройки расположения файла снимка, используемого для тестирования сохранением снимков. По умолчанию имя файла снимка совпадает с именем файла точки входа с расширением .snapshot.
Класс: MockFunctionContext
Класс MockFunctionContext используется для проверки или изменения поведения моков, созданных с помощью API MockTracker.
ctx.calls
Метод-геттер, возвращающий копию внутреннего массива, используемого для отслеживания вызовов мока. Каждый элемент массива — объект со следующими свойствами.
-
arguments<Массив> Массив аргументов, переданных в функцию мока. -
error<любой> Если функция мока выбросила исключение, то это свойство содержит выброшенное значение. По умолчанию:undefined. -
result<любой> Значение, возвращенное функцией мока. -
stack<Ошибка> ОбъектErrorс помощью стека которого можно определить место вызова функции мока. -
target<Функция> | <неопределено> Если функция мока — конструктор, в этом поле содержится класс, который создается. В противном случае это будетundefined. -
this<любой> Значениеthisфункции мока.
ctx.callCount()
- Возвращает: <целое число> Количество вызовов этого мока.
Функция возвращает количество вызовов данного мока. Эта функция более эффективна, чем проверка ctx.calls.length, потому что ctx.calls — геттер, создающий копию внутреннего массива отслеживания вызовов.
ctx.mockImplementation(implementation)
-
implementation<Функция> | <Асинхронная функция> Функция, которая будет использоваться в качестве новой реализации мока.
Эта функция используется для изменения поведения существующего мока.
Следующий пример создаёт функцию-моковую функцию с помощью t.mock.fn(), вызывает её, а затем изменяет реализацию мока на другую функцию.
test('changes a mock behavior', (t) => {
let cnt = 0;
function addOne() {
cnt++;
return cnt;
}
function addTwo() {
cnt += 2;
return cnt;
}
const fn = t.mock.fn(addOne);
assert.strictEqual(fn(), 1);
fn.mock.mockImplementation(addTwo);
assert.strictEqual(fn(), 3);
assert.strictEqual(fn(), 5);
}); copy
ctx.mockImplementationOnce(implementation[, onCall])
-
implementation<Функция> | <Асинхронная функция> Функция, которая будет использоваться в качестве реализации мока для вызова с номером, указанным вonCall. -
onCall<целое число> Номер вызова, который будет использовать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);
}); copy
ctx.resetCalls()
Сбрасывает историю вызовов функции мока.
ctx.restore()
Сбрасывает реализацию функции мока до её исходного поведения. После вызова этой функции мок всё ещё может использоваться.
Класс: MockModuleContext
Класс MockModuleContext используется для управления поведением модульных моков, созданных с помощью API MockTracker.
ctx.restore()
Сбрасывает реализацию модульного мока.
Класс: MockTracker
Класс MockTracker используется для управления функциональностью подмены. Модуль тестового запуска предоставляет экспорт верхнего уровня mock, который является экземпляром MockTracker. Каждый тест также предоставляет свой экземпляр MockTracker через свойство контекста теста mock.
Опции реализации подмены функции
- Опциональный метод для создания подмены. По умолчанию: функция без действий.<Function> | <AsyncFunction>
-
Опциональный метод, используемый как реализация подмены для
original. Это полезно для создания подмен, которые демонстрируют одно поведение для определенного числа вызовов, а затем восстанавливают поведениеoriginal. По умолчанию: метод, указанный вoriginal.<Function> | <AsyncFunction> -
Дополнительные параметры конфигурации для подмены функции. Поддерживаются следующие свойства:<Object>
-
Количество раз, когда подмена будет использовать поведение
implementation. После того, как функция подмены будет вызванаtimesраз, она автоматически восстановит поведениеoriginal. Это значение должно быть целым числом, большим нуля. По умолчанию:Infinity.<integer>
-
Количество раз, когда подмена будет использовать поведение
- Возвращает: <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);
}); copy Опции реализации подмены свойства-геттера
Эта функция является синтаксическим сахаром для MockTracker.method с options.getter установленным в true.
Опции реализации подмены метода
- Объект, метод которого подменяется.<Object>
-
Идентификатор метода на
objectдля подмены. Еслиobject[methodName]не является функцией, выбрасывается ошибка.<string> | <symbol> -
Опциональный метод, используемый как реализация подмены для
object[methodName]. По умолчанию: исходный метод, указанный вobject[methodName].<Function> | <AsyncFunction> -
Дополнительные параметры конфигурации для подмены метода. Поддерживаются следующие свойства:<Object>
-
Если
true,object[methodName]обрабатывается как геттер. Этот параметр нельзя использовать с параметромsetter.<boolean> По умолчанию: false. -
Если
true,object[methodName]обрабатывается как сеттер. Этот параметр нельзя использовать с параметромgetter.<boolean> По умолчанию: false. -
Количество раз, когда подмена будет использовать поведение
implementation. После того, как подменённый метод был вызванtimesраз, он автоматически восстановит исходное поведение. Это значение должно быть целым числом, большим нуля. По умолчанию:Infinity.<integer>
-
Если
- Возвращает: <Proxy> Подменённый метод. Подменённый метод содержит специальное свойство
mock, которое является экземпляромMockFunctionContextи может использоваться для проверки и изменения поведения подменённого метода.
Эта функция используется для создания подмены на существующем методе объекта. Следующий пример демонстрирует, как создается подмена на существующем методе объекта.
test('spies on an object method', (t) => {
const number = {
value: 5,
subtract(a) {
return this.value - a;
},
};
t.mock.method(number, 'subtract');
assert.strictEqual(number.subtract.mock.calls.length, 0);
assert.strictEqual(number.subtract(3), 2);
assert.strictEqual(number.subtract.mock.calls.length, 1);
const call = number.subtract.mock.calls[0];
assert.deepStrictEqual(call.arguments, [3]);
assert.strictEqual(call.result, 2);
assert.strictEqual(call.error, undefined);
assert.strictEqual(call.target, undefined);
assert.strictEqual(call.this, number);
}); copy Опции подмены модуля
- Идентификатор модуля, который нужно подменить.<string>
-
Дополнительные параметры конфигурации для подмены модуля. Поддерживаются следующие свойства:<Object>
-
Если
false, каждый вызовrequire()илиimport()генерирует новый модуль подмены. Еслиtrue, последующие вызовы вернут один и тот же модуль подмены, и модуль подмены будет добавлен в кэш CommonJS.<boolean> По умолчанию: false. -
Опциональное значение, используемое в качестве значения экспорта по умолчанию подменённого модуля. Если это значение не указано, подмены ESM не включают экспорт по умолчанию. Если подмена является модулем CommonJS или встроенным модулем, это значение используется как значение
module.exports. Если это значение не указано, подмены CJS и встроенные подмены используют пустой объект в качестве значенияmodule.exports.<any> -
Опциональный объект, ключи и значения которого используются для создания именованных экспортов подменённого модуля. Если подмена является модулем CommonJS или встроенным модулем, эти значения копируются в
module.exports. Поэтому, если подмена создается с именованными экспортами и экспортом по умолчанию, отличным от объекта, подмена выбросит исключение при использовании как модуль CJS или встроенный модуль.<Object>
-
Если
- Возвращает: <MockModuleContext> Объект, который можно использовать для управления подменой.
Эта функция используется для подмены экспортов модулей ECMAScript, модулей CommonJS и встроенных модулей Node.js. Любые ссылки на исходный модуль до подмены не затрагиваются. Следующий пример демонстрирует, как создается подмена для модуля.
test('mocks a builtin module in both module systems', async (t) => {
// Create a mock of 'node:readline' with a named export named 'fn', which
// does not exist in the original 'node:readline' module.
const mock = t.mock.module('node:readline', {
namedExports: { fn() { return 42; } },
});
let esmImpl = await import('node:readline');
let cjsImpl = require('node:readline');
// cursorTo() is an export of the original 'node:readline' module.
assert.strictEqual(esmImpl.cursorTo, undefined);
assert.strictEqual(cjsImpl.cursorTo, undefined);
assert.strictEqual(esmImpl.fn(), 42);
assert.strictEqual(cjsImpl.fn(), 42);
mock.restore();
// The mock is restored, so the original builtin module is returned.
esmImpl = await import('node:readline');
cjsImpl = require('node:readline');
assert.strictEqual(typeof esmImpl.cursorTo, 'function');
assert.strictEqual(typeof cjsImpl.cursorTo, 'function');
assert.strictEqual(esmImpl.fn, undefined);
assert.strictEqual(cjsImpl.fn, undefined);
}); copy Сброс подмен
Эта функция восстанавливает исходное поведение всех подмен, которые были ранее созданы этим MockTracker, и отсоединяет подмены от экземпляра MockTracker. После отсоединения подмены всё ещё можно использовать, но экземпляр MockTracker больше не может быть использован для сброса их поведения или взаимодействия с ними.
После завершения каждого теста эта функция вызывается на контексте теста MockTracker. Если глобальный MockTracker используется часто, рекомендуется вызывать эту функцию вручную.
Восстановление всех подмен
Эта функция восстанавливает исходное поведение всех подмен, которые были ранее созданы этим MockTracker. В отличие от mock.reset(), mock.restoreAll() не отсоединяет подмены от экземпляра MockTracker.
Опции реализации подмены свойства-сеттера
Эта функция является синтаксическим сахаром для MockTracker.method с options.setter установленным в true.
Класс: MockTimers
Имитация таймеров — это техника, часто используемая в тестировании программного обеспечения для имитации и управления поведением таймеров, таких как setInterval и setTimeout, без фактического ожидания указанных интервалов времени.
MockTimers также может имитировать объект Date.
Класс MockTracker предоставляет экспорт верхнего уровня timers, который является экземпляром MockTimers.
timers.enable([enableOptions])
Включает имитацию таймеров для указанных таймеров.
-
enableOptions<Объект> Необязательные параметры конфигурации для включения имитации таймеров. Поддерживаются следующие свойства:-
apis<Массив> Необязательный массив, содержащий таймеры для имитации. В настоящее время поддерживаются значения таймеров'setInterval','setTimeout','setImmediate', и'Date'. По умолчанию:['setInterval', 'setTimeout', 'setImmediate', 'Date']. Если массив не предоставлен, все API, связанные со временем ('setInterval','clearInterval','setTimeout','clearTimeout','setImmediate','clearImmediate', и'Date') будут имитироваться по умолчанию. -
now<число> | <Дата> Необязательное число или объект Date, представляющий начальное время (в миллисекундах), используемое в качестве значения дляDate.now(). По умолчанию:0.
-
Примечание: При включении имитации для конкретного таймера его связанная функция clear также будет неявно имитироваться.
Примечание: Имитация Date повлияет на поведение имитируемых таймеров, так как они используют один и тот же внутренний таймер.
Пример использования без установки начального времени:
Модули MJS
import { mock } from 'node:test';
mock.timers.enable({ apis: ['setInterval'] });
Модули CJS
const { mock } = require('node:test');
mock.timers.enable({ apis: ['setInterval'] }); В приведенном выше примере включена имитация таймера setInterval и неявно имитируется функция clearInterval. Будут имитироваться только функции setInterval и clearInterval из node:timers, node:timers/promises и globalThis.
Пример использования с установленным начальным временем
Модули MJS
import { mock } from 'node:test';
mock.timers.enable({ apis: ['Date'], now: 1000 });
Модули CJS
const { mock } = require('node:test');
mock.timers.enable({ apis: ['Date'], now: 1000 }); Пример использования с установленным в качестве начального времени объектом Date
Модули MJS
import { mock } from 'node:test';
mock.timers.enable({ apis: ['Date'], now: new Date() });
Модули CJS
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()
Эта функция восстанавливает стандартное поведение всех ранее созданных имитаций данным экземпляром MockTimers и отсоединяет имитации от экземпляра MockTracker.
Примечание: После завершения каждого теста эта функция вызывается для MockTracker контекста теста.
Модули MJS
import { mock } from 'node:test';
mock.timers.reset();
Модули CJS
const { mock } = require('node:test');
mock.timers.reset();
timers[Symbol.dispose]()
Вызывает timers.reset().
timers.tick([milliseconds])
Передвигает время для всех имитированных таймеров.
-
milliseconds<число> Количество времени в миллисекундах для продвижения таймеров. По умолчанию:1.
Примечание: Это отличается от поведения setTimeout в Node.js и принимает только положительные числа. В Node.js, setTimeout с отрицательными числами поддерживается только для совместимости с веб-платформами.
Следующий пример имитирует функцию setTimeout и с помощью .tick переводит время, вызывая все ожидающие таймеры.
Модули MJS
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);
});
Модули CJS
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 может быть вызвана многократно
Модули MJS
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);
});
Модули CJS
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 также был установлен для имитации).
Модули MJS
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);
});
Модули CJS
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);
}); Использование функций clear
Как уже упоминалось, все функции clear из таймеров (clearTimeout, clearInterval, и clearImmediate) неявно имитируются. Посмотрите на этот пример с использованием setTimeout:
Модули MJS
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);
});
Модули CJS
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.
Модули MJS
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);
});
Модули CJS
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:
Модули MJS
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));
}
});
Модули CJS
const assert = require('node:assert');
const { test } = require('node:test');
const nodeTimersPromises = require('node:timers/promises');
test('should tick five times testing a real use case', async (context) => {
context.mock.timers.enable({ apis: ['setInterval'] });
const expectedIterations = 3;
const interval = 1000;
const startedAt = Date.now();
async function run() {
const times = [];
for await (const time of nodeTimersPromises.setInterval(interval, startedAt)) {
times.push(time);
if (times.length === expectedIterations) break;
}
return times;
}
const r = run();
context.mock.timers.tick(interval);
context.mock.timers.tick(interval);
context.mock.timers.tick(interval);
const timeResults = await r;
assert.strictEqual(timeResults.length, expectedIterations);
for (let it = 1; it < expectedIterations; it++) {
assert.strictEqual(timeResults[it - 1], startedAt + (interval * it));
}
});
timers.runAll()
Немедленно запускает все ожидающие имитированные таймеры. Если объект Date также имитируется, он также продвинет объект Date до времени самого позднего таймера.
В приведенном ниже примере все ожидающие таймеры запускаются немедленно, заставляя их выполняться без задержки.
Модули MJS
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);
});
Модули CJS
const assert = require('node:assert');
const { test } = require('node:test');
test('runAll functions following the given order', (context) => {
context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
const results = [];
setTimeout(() => results.push(1), 9999);
// Notice that if both timers have the same timeout,
// the order of execution is guaranteed
setTimeout(() => results.push(3), 8888);
setTimeout(() => results.push(2), 8888);
assert.deepStrictEqual(results, []);
context.mock.timers.runAll();
assert.deepStrictEqual(results, [3, 2, 1]);
// The Date object is also advanced to the furthest timer's time
assert.strictEqual(Date.now(), 9999);
}); Примечание: Функция runAll() специально разработана для запуска таймеров в контексте имитации таймеров. Она не оказывает никакого влияния на реальные системные часы или реальные таймеры за пределами среды имитации.
timers.setTime(milliseconds)
Устанавливает текущую метку времени Unix, которая будет использоваться в качестве ссылки для любых имитированных объектов Date.
Модули MJS
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);
});
Модули CJS
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.
Модули MJS
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);
});
Модули CJS
const assert = require('node:assert');
const { test } = require('node:test');
test('runAll functions following the given order', (context) => {
context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
const results = [];
setTimeout(() => results.push(1), 9999);
assert.deepStrictEqual(results, []);
context.mock.timers.setTime(12000);
assert.deepStrictEqual(results, []);
// The date is advanced but the timers don't tick
assert.strictEqual(Date.now(), 12000);
}); Класс: TestsStream
- Расширяет <Readable>
Успешное вызов метода run() вернёт новый объект <TestsStream>, стримингующий серию событий, представляющих выполнение тестов. TestsStream будут испускать события в порядке определения тестов.
Некоторые события гарантированно испускаются в том же порядке, что и определение тестов, в то время как другие испускаются в порядке их выполнения.
Событие: 'test:coverage'
-
data<Объект>-
summary<Объект> Объект, содержащий отчёт о покрытии кода.-
files<Массив> Массив отчётов о покрытии для отдельных файлов. Каждый отчёт — это объект со следующей схемой:-
path<строка> Абсолютный путь к файлу. -
totalLineCount<число> Общее количество строк. -
totalBranchCount<число> Общее количество ветвей. -
totalFunctionCount<число> Общее количество функций. -
coveredLineCount<число> Количество покрытых строк. -
coveredBranchCount<число> Количество покрытых ветвей. -
coveredFunctionCount<число> Количество покрытых функций. -
coveredLinePercent<число> Процент покрытых строк. -
coveredBranchPercent<число> Процент покрытых ветвей. -
coveredFunctionPercent<число> Процент покрытых функций. -
functions<Массив> Массив функций, представляющих покрытие функций. -
branches<Массив> Массив ветвей, представляющих покрытие ветвей. -
lines<Массив> Массив строк, представляющих номера строк и количество раз, когда они были покрыты.
-
-
totals<Объект> Объект, содержащий сводку по покрытию всех файлов.-
totalLineCount<число> Общее количество строк. -
totalBranchCount<число> Общее количество ветвей. -
totalFunctionCount<число> Общее количество функций. -
coveredLineCount<число> Количество покрытых строк. -
coveredBranchCount<число> Количество покрытых ветвей. -
coveredFunctionCount<число> Количество покрытых функций. -
coveredLinePercent<число> Процент покрытых строк. -
coveredBranchPercent<число> Процент покрытых ветвей. -
coveredFunctionPercent<число> Процент покрытых функций.
-
-
workingDirectory<строка> Рабочая директория, когда началось измерение покрытия кода. Это полезно для отображения относительных путей в случае, если тесты изменили рабочую директорию процесса Node.js.
-
-
nesting<число> Уровень вложенности теста.
-
Выпущено, когда включено покрытие кода и все тесты завершены.
Событие: '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> Уровень вложенности теста. -
testNumber<number> Порядковый номер теста. -
todo<string> | <boolean> | <undefined> Присутствует, если вызвана функцияcontext.todo -
skip<string> | <boolean> | <undefined> Присутствует, если вызвана функцияcontext.skip
-
-
'test:pass' -
'test:fail'
Выводится, когда тест завершает своё выполнение. Это событие не выводится в том же порядке, что и определение тестов. Соответствующие события в порядке объявления '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> Уровень вложенности теста.
-
Выводится, когда тест из очереди, непосредственно перед его выполнением. Это событие не гарантируется в том же порядке, что и порядок определения тестов. Соответствующее событие в порядке объявления '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> Уровень вложенности теста.
-
Выводится, когда вызывается 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> Уровень вложенности теста.
-
Выводится, когда тест помещён в очередь для выполнения.
Событие: 'test:fail'
-
data<Объект>-
column<число> | <неопределено> Номер столбца, в котором определён тест, илиundefined, если тест был запущен через REPL. -
details<Объект> Дополнительные метаданные выполнения.-
duration_ms<число> Длительность теста в миллисекундах. -
error<Ошибка> Ошибка, обертывающая ошибку, брошенную тестом.-
cause<Ошибка> Фактическая ошибка, брошенная тестом.
-
-
type<строка> | <неопределено> Тип теста, используемый для обозначения является ли это набором тестов.
-
-
file<строка> | <неопределено> Путь к файлу теста,undefinedесли тест был запущен через REPL. -
line<число> | <неопределено> Номер строки, в которой определён тест, илиundefined, если тест был запущен через REPL. -
name<строка> Название теста. -
nesting<число> Уровень вложенности теста. -
testNumber<число> Порядковый номер теста. -
todo<строка> | <булево> | <неопределено> Присутствует, если вызванcontext.todo -
skip<строка> | <булево> | <неопределено> Присутствует, если вызванcontext.skip
-
Выводится, когда тест завершается неудачно. Этот событие гарантированно будет выведено в том же порядке, что и определение тестов. Соответствующее событие, упорядоченное по выполнению, является 'test:complete'.
Событие: 'test:pass'
-
data<Объект>-
column<число> | <неопределено> Номер столбца, в котором определён тест, илиundefined, если тест был запущен через REPL. -
details<Объект> Дополнительные метаданные выполнения.-
duration_ms<число> Длительность теста в миллисекундах. -
type<строка> | <неопределено> Тип теста, используемый для обозначения является ли это набором тестов.
-
-
file<строка> | <неопределено> Путь к файлу теста,undefinedесли тест был запущен через REPL. -
line<число> | <неопределено> Номер строки, в которой определён тест, илиundefined, если тест был запущен через REPL. -
name<строка> Название теста. -
nesting<число> Уровень вложенности теста. -
testNumber<число> Порядковый номер теста. -
todo<строка> | <булево> | <неопределено> Присутствует, если вызванcontext.todo -
skip<строка> | <булево> | <неопределено> Присутствует, если вызванcontext.skip
-
Выводится, когда тест завершается успешно. Это событие гарантированно будет выведено в том же порядке, что и определение тестов. Соответствующее событие, упорядоченное по выполнению, является 'test:complete'.
Событие: 'test:plan'
-
data<Объект>-
column<число> | <неопределено> Номер столбца, в котором определён тест, илиundefined, если тест был запущен через REPL. -
file<строка> | <неопределено> Путь к файлу теста,undefinedесли тест был запущен через REPL. -
line<число> | <неопределено> Номер строки, в которой определён тест, илиundefined, если тест был запущен через REPL. -
nesting<число> Уровень вложенности теста. -
count<число> Количество подтестов, которые были выполнены.
-
Выводится, когда все подтесты завершены для данного теста. Это событие гарантированно будет выведено в том же порядке, что и определение тестов.
Событие: 'test:start'
-
data<Объект>-
column<число> | <неопределено> Номер столбца, в котором определён тест, илиundefinedесли тест был запущен через REPL. -
file<строка> | <неопределено> Путь к файлу с тестом,undefinedесли тест был запущен через REPL. -
line<число> | <неопределено> Номер строки, в которой определён тест, илиundefinedесли тест был запущен через REPL. -
name<строка> Название теста. -
nesting<число> Уровень вложенности теста.
-
Вызывается, когда тест начинает сообщать о своём статусе и статусе своих подтестов. Этот событие гарантированно вызывается в том же порядке, что и определение тестов. Соответствующее событие последовательности выполнения — 'test:dequeue'.
Событие: 'test:stderr'
Вызывается, когда выполняющийся тест записывает в stderr. Это событие вызывается только если передан флаг --test. Это событие не гарантированно вызывается в том же порядке, что и определение тестов.
Событие: 'test:stdout'
Вызывается, когда выполняющийся тест записывает в stdout. Это событие вызывается только если передан флаг --test. Это событие не гарантированно вызывается в том же порядке, что и определение тестов.
Событие: 'test:watch:drained'
Вызывается, когда в режиме наблюдения больше нет тестов в очереди на выполнение.
Класс: TestContext
Экземпляр TestContext передается каждой тестовой функции для взаимодействия с тестовым загрузчиком. Однако конструктор TestContext не доступен как часть API.
context.before([fn][, options])
-
fn<Функция> | <Асинхронная функция> Функция-обработчик. Первым аргументом этой функции является объектTestContext. Если обработчик использует колбэки, функция колбэка передается в качестве второго аргумента. По умолчанию: функция-пустышка. -
options<Объект> Параметры конфигурации для обработчика. Поддерживаются следующие свойства:-
signal<AbortSignal> Позволяет прервать выполнение обработчика. -
timeout<число> Количество миллисекунд, после которого обработчик завершится с ошибкой. Если не указано, подтесты наследуют это значение от родительского теста. По умолчанию:Infinity.
-
Эта функция используется для создания обработчика, выполняющегося перед подтестом текущего теста.
context.beforeEach([fn][, options])
-
fn<Функция> | <Асинхронная функция> Функция-обработчик. Первым аргументом этой функции является объектTestContext. Если обработчик использует колбэки, функция колбэка передается в качестве второго аргумента. По умолчанию: функция-пустышка. -
options<Объект> Параметры конфигурации для обработчика. Поддерживаются следующие свойства:-
signal<AbortSignal> Позволяет прервать выполнение обработчика. -
timeout<число> Количество миллисекунд, после которого обработчик завершится с ошибкой. Если не указано, подтесты наследуют это значение от родительского теста. По умолчанию:Infinity.
-
Эта функция используется для создания обработчика, выполняющегося перед каждым подтестом текущего теста.
test('top level test', async (t) => {
t.beforeEach((t) => t.diagnostic(`about to run ${t.name}`));
await t.test(
'This is a subtest',
(t) => {
assert.ok('some relevant assertion here');
},
);
}); copy
context.after([fn][, options])
-
fn<Функция> | <Асинхронная функция> Функция-обработчик. Первым аргументом этой функции является объектTestContext. Если обработчик использует колбэки, функция колбэка передается в качестве второго аргумента. По умолчанию: функция-пустышка. -
options<Объект> Параметры конфигурации для обработчика. Поддерживаются следующие свойства:-
signal<AbortSignal> Позволяет прервать выполнение обработчика. -
timeout<число> Количество миллисекунд, после которого обработчик завершится с ошибкой. Если не указано, подтесты наследуют это значение от родительского теста. По умолчанию:Infinity.
-
Эта функция используется для создания обработчика, выполняющегося после завершения текущего теста.
test('top level test', async (t) => {
t.after((t) => t.diagnostic(`finished running ${t.name}`));
assert.ok('some relevant assertion here');
}); copy
context.afterEach([fn][, options])
-
fn<Функция> | <Асинхронная функция> Функция-обработчик. Первым аргументом этой функции является объектTestContext. Если обработчик использует колбэки, функция колбэка передается в качестве второго аргумента. По умолчанию: функция-пустышка. -
options<Объект> Параметры конфигурации для обработчика. Поддерживаются следующие свойства:-
signal<AbortSignal> Позволяет прервать выполнение обработчика. -
timeout<число> Количество миллисекунд, после которого обработчик завершится с ошибкой. Если не указано, подтесты наследуют это значение от родительского теста. По умолчанию:Infinity.
-
Эта функция используется для создания обработчика, выполняющегося после каждого подтеста текущего теста.
test('top level test', async (t) => {
t.afterEach((t) => t.diagnostic(`finished running ${t.name}`));
await t.test(
'This is a subtest',
(t) => {
assert.ok('some relevant assertion here');
},
);
}); copy
context.assert
Объект, содержащий методы проверки, привязанные к context. Здесь доступны функции верхнего уровня из модуля node:assert для создания планов тестов.
test('test', (t) => {
t.plan(1);
t.assert.strictEqual(true, true);
}); copy
context.assert.snapshot(value[, options])
-
value<любое> Значение, которое необходимо сериализовать в строку. Если Node.js был запущен со флагом--test-update-snapshots, сериализованное значение записывается в файл снимков. В противном случае сериализованное значение сравнивается с соответствующим значением в существующем файле снимков. -
options<Объект> Необязательные параметры конфигурации. Поддерживаются следующие свойства:-
serializers<Массив> Массив синхронных функций, используемых для сериализации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)]
});
}); copy
context.diagnostic(message)
-
message<строка> Сообщение, которое необходимо сообщить.
Эта функция используется для записи диагностической информации в вывод. Любая диагностическая информация включается в конце результатов теста. Эта функция не возвращает значение.
test('top level test', (t) => {
t.diagnostic('A diagnostic message');
}); copy
context.fullName
Имя теста и каждого из его предков, разделенные >.
context.name
Имя теста.
context.plan(count)
-
count<число> Количество ожидаемых проверок и подтестов.
Эта функция используется для задания количества ожидаемых проверок и подтестов, которые должны быть выполнены в рамках теста. Если фактическое количество проверок и подтестов не совпадает с ожидаемым значением, тест завершится с ошибкой.
Примечание: Для отслеживания проверок необходимо использовать функцию
t.assert, а неassertнапрямую.
test('top level test', (t) => {
t.plan(2);
t.assert.ok('some relevant assertion here');
t.test('subtest', () => {});
}); copy При работе с асинхронным кодом функция 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();
});
}); copy
context.runOnly(shouldRunOnlyTests)
-
shouldRunOnlyTests<логическое значение> Указывает, нужно ли запускать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 }),
]);
}); copy
context.signal
- Тип: <AbortSignal>
Может использоваться для прерывания подзадач теста, когда тест был прерван.
test('top level test', async (t) => {
await fetch('some/uri', { signal: t.signal });
}); copy
context.skip([message])
-
message<string> Дополнительное сообщение о пропуске.
Эта функция указывает, что тест пропущен. Если message предоставлено, оно включается в вывод. Вызов skip() не завершает выполнение функции теста. Эта функция не возвращает значение.
test('top level test', (t) => {
// Make sure to return here as well if the test contains additional logic.
t.skip('this is skipped');
}); copy
context.todo([message])
-
message<string> ДополнительноеTODOсообщение.
Эта функция добавляет TODO директиву в вывод теста. Если message предоставлено, оно включается в вывод. Вызов todo() не завершает выполнение функции теста. Эта функция не возвращает значение.
test('top level test', (t) => {
// This test is marked as `TODO`
t.todo('this is a todo');
}); copy
context.test([name][, options][, fn])
-
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. -
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: 4 },
(t) => {
assert.ok('some relevant assertion here');
},
);
}); copy Класс: SuiteContext
Экземпляр SuiteContext передается каждой функции набора, чтобы взаимодействовать с запуском теста. Однако конструктор SuiteContext не доступен как часть API.
context.name
Имя набора.
context.signal
- Тип: <AbortSignal>
Может использоваться для прерывания подзадач теста, когда тест был прерван.
© 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