Mocking in tests

Mocking is a means of creating a facsimile, a puppet. This is generally done in a when 'a', do 'b' manner of puppeteering. The idea is to limit the number of moving pieces and control things that "don't matter". "mocks" and "stubs" are technically different kinds of "test doubles". For the curious mind, a stub is a replacement that does nothing (a no-op) but track its invocation. A mock is a stub that also has a fake implementation (the when 'a', do 'b'). Within this doc, the difference is unimportant, and stubs are referred to as mocks.

Tests should be deterministic: runnable in any order, any number of times, and always produce the same result. Proper setup and mocking make this possible.

Node.js provides many ways to mock various pieces of code.

This articles deals with the following types of tests:

typedescriptionexamplemock candidates
unitthe smallest bit of code you can isolateconst sum = (a, b) => a + bown code, external code, external system
componenta unit + dependenciesconst arithmetic = (op = sum, a, b) => ops[op](a, b)external code, external system
integrationcomponents fitting together-external code, external system
end-to-end (e2e)app + external data stores, delivery, etcA fake user (ex a Playwright agent) literally using an app connected to real external systems.none (do not mock)

There are different schools of thought about when to mock and when not to mock, the broad strokes of which are outlined below.

When and not to mock

There are 3 main mock candidates:

  • Own code
  • External code
  • External system

Own code

This is what your project controls.

import import foofoo from './foo.mjs';

export function function main(): voidmain() {
  const const f: anyf = import foofoo();
}

Here, foo is an "own code" dependency of main.

Why

For a true unit test of main, foo should be mocked: you're testing that main works, not that main + foo work (that's a different test).

Why not

Mocking foo can be more trouble than worth, especially when foo is simple, well-tested, and rarely updated.

Not mocking foo can be better because it's more authentic and increases coverage of foo (because main's tests will also verify foo). This can, however, create noise: when foo breaks, a bunch of other tests will also break, so tracking down the problem is more tedious: if only the 1 test for the item ultimately responsible for the issue is failing, that's very easy to spot; whereas 100 tests failing creates a needle-in-a-haystack to find the real problem.

External code

This is what your project does not control.

import import barbar from 'bar';

export function function main(): voidmain() {
  const const f: anyf = import barbar();
}

Here, bar is an external package, e.g. an npm dependency.

Uncontroversially, for unit tests, this should always be mocked. For component and integration tests, whether to mock depends on what this is.

Why

Verifying that code that your project does not maintain works is not the goal of a unit test (and that code should have its own tests).

Why not

Sometimes, it's just not realistic to mock. For example, you would almost never mock a large framework such as react or angular (the medicine would be worse than the ailment).

External system

These are things like databases, environments (Chromium or Firefox for a web app, an operating system for a node app, etc), file systems, memory store, etc.

Ideally, mocking these would not be necessary. Aside from somehow creating isolated copies for each case (usually very impractical due to cost, additional execution time, etc), the next best option is to mock. Without mocking, tests sabotage each other:

import { import dbdb } from 'db';

export function function read(key: any, all?: boolean): anyread(key: anykey, all: booleanall = false) {
  validate(key: anykey, val);

  if (all: booleanall) {
    return import dbdb.getAll(key: anykey);
  }

  return import dbdb.getOne(key: anykey);
}

export function function save(key: any, val: any): anysave(key: anykey, val: anyval) {
  validate(key: anykey, val: anyval);

  return import dbdb.upsert(key: anykey, val: anyval);
}

In the above, the first and second cases (the it() statements) can sabotage each other because they are run concurrently and mutate the same store (a race condition): save()'s insertion can cause the otherwise valid read()'s test to fail its assertion on items found (and read()'s can do the same thing to save()'s).

What to mock

Modules + units

This leverages mock from the Node.js test runner.

import 
const assert: Omit<typeof assert, "equal" | "notEqual" | "deepEqual" | "notDeepEqual" | "ok" | "strictEqual" | "deepStrictEqual" | "ifError" | "strict" | "AssertionError"> & {
    ...;
}
In strict assertion mode, non-strict methods behave like their corresponding strict methods. For example, {@link deepEqual } will behave like {@link deepStrictEqual } . In strict assertion mode, error messages for objects display a diff. In legacy assertion mode, error messages for objects display the objects, often truncated. To use strict assertion mode: ```js import { strict as assert } from 'node:assert'; import assert from 'node:assert/strict'; ``` Example error diff: ```js import { strict as assert } from 'node:assert'; assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); // AssertionError: Expected inputs to be strictly deep-equal: // + actual - expected ... Lines skipped // // [ // [ // ... // 2, // + 3 // - '3' // ], // ... // 5 // ] ``` To deactivate the colors, use the `NO_COLOR` or `NODE_DISABLE_COLORS` environment variables. This will also deactivate the colors in the REPL. For more on color support in terminal environments, read the tty `getColorDepth()` documentation.
@sincev15.0.0, v13.9.0, v12.16.2, v9.9.0
assert
from 'node:assert/strict';
import { function before(fn?: it.HookFn, options?: it.HookOptions): void
This function creates a hook that runs before executing a suite. ```js describe('tests', async () => { before(() => console.log('about to run some test')); it('is a subtest', () => { assert.ok('some relevant assertion here'); }); }); ```
@sincev18.8.0, v16.18.0@paramfn The hook function. If the hook uses callbacks, the callback function is passed as the second argument.@paramoptions Configuration options for the hook.
before
, function describe(name?: string, options?: it.TestOptions, fn?: it.SuiteFn): Promise<void> (+3 overloads)
The `suite()` function is imported from the `node:test` module.
@paramname The name of the suite, which is displayed when reporting test results. Defaults to the `name` property of `fn`, or `'<anonymous>'` if `fn` does not have a name.@paramoptions Configuration options for the suite. This supports the same options as {@link test}.@paramfn The suite function declaring nested tests and suites. The first argument to this function is a {@link SuiteContext} object.@returnImmediately fulfilled with `undefined`.@sincev20.13.0
describe
, function it(name?: string, fn?: it.TestFn): Promise<void> (+3 overloads)
The `test()` function is the value imported from the `test` module. Each invocation of this function results in reporting the test to the `TestsStream`. The `TestContext` object passed to the `fn` argument can be used to perform actions related to the current test. Examples include skipping the test, adding additional diagnostic information, or creating subtests. `test()` returns a `Promise` that fulfills once the test completes. if `test()` is called within a suite, it fulfills immediately. The return value can usually be discarded for top level tests. However, the return value from subtests should be used to prevent the parent test from finishing first and cancelling the subtest as shown in the following example. ```js 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); }); }); }); ``` The `timeout` option can be used to fail the test if it takes longer than `timeout` milliseconds to complete. However, it is not a reliable mechanism for canceling tests because a running test might block the application thread and thus prevent the scheduled cancellation.
@sincev18.0.0, v16.17.0@paramname The name of the test, which is displayed when reporting test results. Defaults to the `name` property of `fn`, or `'<anonymous>'` if `fn` does not have a name.@paramoptions Configuration options for the test.@paramfn The function under test. The first argument to this function is a {@link TestContext } object. If the test uses callbacks, the callback function is passed as the second argument.@returnFulfilled with `undefined` once the test completes, or immediately if the test runs within a suite.
it
, const mock: it.MockTrackermock } from 'node:test';
function describe(name?: string, options?: it.TestOptions, fn?: it.SuiteFn): Promise<void> (+3 overloads)
The `suite()` function is imported from the `node:test` module.
@paramname The name of the suite, which is displayed when reporting test results. Defaults to the `name` property of `fn`, or `'<anonymous>'` if `fn` does not have a name.@paramoptions Configuration options for the suite. This supports the same options as {@link test}.@paramfn The suite function declaring nested tests and suites. The first argument to this function is a {@link SuiteContext} object.@returnImmediately fulfilled with `undefined`.@sincev20.13.0
describe
('foo', { test.TestOptions.concurrency?: number | boolean | undefined
If a number is provided, then that many tests would run in parallel. If truthy, it would run (number of cpu cores - 1) tests in parallel. For subtests, it will be `Infinity` tests in parallel. If falsy, it would only run one test at a time. If unspecified, subtests inherit this value from their parent.
@defaultfalse
concurrency
: true }, () => {
const const barMock: it.Mock<(...args: any[]) => undefined>barMock = const mock: it.MockTrackermock.test.MockTracker.fn<(...args: any[]) => undefined>(original?: ((...args: any[]) => undefined) | undefined, options?: it.MockFunctionOptions): it.Mock<(...args: any[]) => undefined> (+1 overload)
This function is used to create a mock function. The following example creates a mock function that increments a counter by one on each invocation. The `times` option is used to modify the mock behavior such that the first two invocations add two to the counter instead of one. ```js 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); }); ```
@sincev19.1.0, v18.13.0@paramoriginal An optional function to create a mock on.@paramimplementation An optional function used as the mock implementation for `original`. This is useful for creating mocks that exhibit one behavior for a specified number of calls and then restore the behavior of `original`.@paramoptions Optional configuration options for the mock function.@returnThe mocked function. The mocked function contains a special `mock` property, which is an instance of {@link MockFunctionContext}, and can be used for inspecting and changing the behavior of the mocked function.
fn
();
let let foo: anyfoo; function before(fn?: it.HookFn, options?: it.HookOptions): void
This function creates a hook that runs before executing a suite. ```js describe('tests', async () => { before(() => console.log('about to run some test')); it('is a subtest', () => { assert.ok('some relevant assertion here'); }); }); ```
@sincev18.8.0, v16.18.0@paramfn The hook function. If the hook uses callbacks, the callback function is passed as the second argument.@paramoptions Configuration options for the hook.
before
(async () => {
const const barNamedExports: anybarNamedExports = await import('./bar.mjs') // discard the original default export .Promise<any>.then<any, never>(onfulfilled?: ((value: any) => any) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<any>
Attaches callbacks for the resolution and/or rejection of the Promise.
@paramonfulfilled The callback to execute when the Promise is resolved.@paramonrejected The callback to execute when the Promise is rejected.@returnsA Promise for the completion of which ever callback is executed.
then
(({ default: _: any_, ...rest: anyrest }) => rest: anyrest);
// It's usually not necessary to manually call restore() after each // nor reset() after all (node does this automatically). const mock: it.MockTrackermock.test.MockTracker.module(specifier: string, options?: it.MockModuleOptions): it.MockModuleContext
This function is used to mock the exports of ECMAScript modules, CommonJS modules, and Node.js builtin modules. Any references to the original module prior to mocking are not impacted. Only available through the [--experimental-test-module-mocks](https://nodejs.org/api/cli.html#--experimental-test-module-mocks) flag.
@sincev22.3.0@experimental@paramspecifier A string identifying the module to mock.@paramoptions Optional configuration options for the mock module.
module
('./bar.mjs', {
test.MockModuleOptions.defaultExport?: any
The value to use as the mocked module's default export. If this value is not provided, ESM mocks do not include a default export. If the mock is a CommonJS or builtin module, this setting is used as the value of `module.exports`. If this value is not provided, CJS and builtin mocks use an empty object as the value of `module.exports`.
defaultExport
: const barMock: it.Mock<(...args: any[]) => undefined>barMock,
// Keep the other exports that you don't want to mock. test.MockModuleOptions.namedExports?: object | undefined
An object whose keys and values are used to create the named exports of the mock module. If the mock is a CommonJS or builtin module, these values are copied onto `module.exports`. Therefore, if a mock is created with both named exports and a non-object default export, the mock will throw an exception when used as a CJS or builtin module.
namedExports
: const barNamedExports: anybarNamedExports,
}); // This MUST be a dynamic import because that is the only way to ensure the // import starts after the mock has been set up. ({ foo: anyfoo } = await import('./foo.mjs')); }); function it(name?: string, fn?: it.TestFn): Promise<void> (+3 overloads)
The `test()` function is the value imported from the `test` module. Each invocation of this function results in reporting the test to the `TestsStream`. The `TestContext` object passed to the `fn` argument can be used to perform actions related to the current test. Examples include skipping the test, adding additional diagnostic information, or creating subtests. `test()` returns a `Promise` that fulfills once the test completes. if `test()` is called within a suite, it fulfills immediately. The return value can usually be discarded for top level tests. However, the return value from subtests should be used to prevent the parent test from finishing first and cancelling the subtest as shown in the following example. ```js 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); }); }); }); ``` The `timeout` option can be used to fail the test if it takes longer than `timeout` milliseconds to complete. However, it is not a reliable mechanism for canceling tests because a running test might block the application thread and thus prevent the scheduled cancellation.
@sincev18.0.0, v16.17.0@paramname The name of the test, which is displayed when reporting test results. Defaults to the `name` property of `fn`, or `'<anonymous>'` if `fn` does not have a name.@paramoptions Configuration options for the test.@paramfn The function under test. The first argument to this function is a {@link TestContext } object. If the test uses callbacks, the callback function is passed as the second argument.@returnFulfilled with `undefined` once the test completes, or immediately if the test runs within a suite.
it
('should do the thing', () => {
const barMock: it.Mock<(...args: any[]) => undefined>barMock.mock: it.MockFunctionContext<(...args: any[]) => undefined>mock.test.MockFunctionContext<(...args: any[]) => undefined>.mockImplementationOnce(implementation: (...args: any[]) => undefined, onCall?: number): void
This function is used to change the behavior of an existing mock for a single invocation. Once invocation `onCall` has occurred, the mock will revert to whatever behavior it would have used had `mockImplementationOnce()` not been called. The following example creates a mock function using `t.mock.fn()`, calls the mock function, changes the mock implementation to a different function for the next invocation, and then resumes its previous behavior. ```js 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); }); ```
@sincev19.1.0, v18.13.0@paramimplementation The function to be used as the mock's implementation for the invocation number specified by `onCall`.@paramonCall The invocation number that will use `implementation`. If the specified invocation has already occurred then an exception is thrown.
mockImplementationOnce
(function function (local function) bar_mock(): undefinedbar_mock() {
/* … */ });
const assert: Omit<typeof assert, "equal" | "notEqual" | "deepEqual" | "notDeepEqual" | "ok" | "strictEqual" | "deepStrictEqual" | "ifError" | "strict" | "AssertionError"> & {
    ...;
}
In strict assertion mode, non-strict methods behave like their corresponding strict methods. For example, {@link deepEqual } will behave like {@link deepStrictEqual } . In strict assertion mode, error messages for objects display a diff. In legacy assertion mode, error messages for objects display the objects, often truncated. To use strict assertion mode: ```js import { strict as assert } from 'node:assert'; import assert from 'node:assert/strict'; ``` Example error diff: ```js import { strict as assert } from 'node:assert'; assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); // AssertionError: Expected inputs to be strictly deep-equal: // + actual - expected ... Lines skipped // // [ // [ // ... // 2, // + 3 // - '3' // ], // ... // 5 // ] ``` To deactivate the colors, use the `NO_COLOR` or `NODE_DISABLE_COLORS` environment variables. This will also deactivate the colors in the REPL. For more on color support in terminal environments, read the tty `getColorDepth()` documentation.
@sincev15.0.0, v13.9.0, v12.16.2, v9.9.0
assert
.equal: <42>(actual: unknown, expected: 42, message?: string | Error) => asserts actual is 42
Tests strict equality between the `actual` and `expected` parameters as determined by [`Object.is()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). ```js import assert from 'node:assert/strict'; assert.strictEqual(1, 2); // AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal: // // 1 !== 2 assert.strictEqual(1, 1); // OK assert.strictEqual('Hello foobar', 'Hello World!'); // AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal: // + actual - expected // // + 'Hello foobar' // - 'Hello World!' // ^ const apples = 1; const oranges = 2; assert.strictEqual(apples, oranges, `apples ${apples} !== oranges ${oranges}`); // AssertionError [ERR_ASSERTION]: apples 1 !== oranges 2 assert.strictEqual(1, '1', new TypeError('Inputs are not identical')); // TypeError: Inputs are not identical ``` If the values are not strictly equal, an `AssertionError` is thrown with a `message` property set equal to the value of the `message` parameter. If the `message` parameter is undefined, a default error message is assigned. If the `message` parameter is an instance of an `Error` then it will be thrown instead of the `AssertionError`.
@sincev0.1.21
equal
(let foo: anyfoo(), 42);
}); });

APIs

A little-known fact is that there is a builtin way to mock fetch. undici is the Node.js implementation of fetch. It's shipped with node, but not currently exposed by node itself, so it must be installed (ex npm install undici).

import 
const assert: Omit<typeof assert, "equal" | "notEqual" | "deepEqual" | "notDeepEqual" | "ok" | "strictEqual" | "deepStrictEqual" | "ifError" | "strict" | "AssertionError"> & {
    ...;
}
In strict assertion mode, non-strict methods behave like their corresponding strict methods. For example, {@link deepEqual } will behave like {@link deepStrictEqual } . In strict assertion mode, error messages for objects display a diff. In legacy assertion mode, error messages for objects display the objects, often truncated. To use strict assertion mode: ```js import { strict as assert } from 'node:assert'; import assert from 'node:assert/strict'; ``` Example error diff: ```js import { strict as assert } from 'node:assert'; assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); // AssertionError: Expected inputs to be strictly deep-equal: // + actual - expected ... Lines skipped // // [ // [ // ... // 2, // + 3 // - '3' // ], // ... // 5 // ] ``` To deactivate the colors, use the `NO_COLOR` or `NODE_DISABLE_COLORS` environment variables. This will also deactivate the colors in the REPL. For more on color support in terminal environments, read the tty `getColorDepth()` documentation.
@sincev15.0.0, v13.9.0, v12.16.2, v9.9.0
assert
from 'node:assert/strict';
import { function beforeEach(fn?: it.HookFn, options?: it.HookOptions): void
This function creates a hook that runs before each test in the current suite. ```js describe('tests', async () => { beforeEach(() => console.log('about to run a test')); it('is a subtest', () => { assert.ok('some relevant assertion here'); }); }); ```
@sincev18.8.0, v16.18.0@paramfn The hook function. If the hook uses callbacks, the callback function is passed as the second argument.@paramoptions Configuration options for the hook.
beforeEach
, function describe(name?: string, options?: it.TestOptions, fn?: it.SuiteFn): Promise<void> (+3 overloads)
The `suite()` function is imported from the `node:test` module.
@paramname The name of the suite, which is displayed when reporting test results. Defaults to the `name` property of `fn`, or `'<anonymous>'` if `fn` does not have a name.@paramoptions Configuration options for the suite. This supports the same options as {@link test}.@paramfn The suite function declaring nested tests and suites. The first argument to this function is a {@link SuiteContext} object.@returnImmediately fulfilled with `undefined`.@sincev20.13.0
describe
, function it(name?: string, fn?: it.TestFn): Promise<void> (+3 overloads)
The `test()` function is the value imported from the `test` module. Each invocation of this function results in reporting the test to the `TestsStream`. The `TestContext` object passed to the `fn` argument can be used to perform actions related to the current test. Examples include skipping the test, adding additional diagnostic information, or creating subtests. `test()` returns a `Promise` that fulfills once the test completes. if `test()` is called within a suite, it fulfills immediately. The return value can usually be discarded for top level tests. However, the return value from subtests should be used to prevent the parent test from finishing first and cancelling the subtest as shown in the following example. ```js 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); }); }); }); ``` The `timeout` option can be used to fail the test if it takes longer than `timeout` milliseconds to complete. However, it is not a reliable mechanism for canceling tests because a running test might block the application thread and thus prevent the scheduled cancellation.
@sincev18.0.0, v16.17.0@paramname The name of the test, which is displayed when reporting test results. Defaults to the `name` property of `fn`, or `'<anonymous>'` if `fn` does not have a name.@paramoptions Configuration options for the test.@paramfn The function under test. The first argument to this function is a {@link TestContext } object. If the test uses callbacks, the callback function is passed as the second argument.@returnFulfilled with `undefined` once the test completes, or immediately if the test runs within a suite.
it
} from 'node:test';
import { import MockAgentMockAgent, import setGlobalDispatchersetGlobalDispatcher } from 'undici'; import import endpointsendpoints from './endpoints.mjs'; function describe(name?: string, options?: it.TestOptions, fn?: it.SuiteFn): Promise<void> (+3 overloads)
The `suite()` function is imported from the `node:test` module.
@paramname The name of the suite, which is displayed when reporting test results. Defaults to the `name` property of `fn`, or `'<anonymous>'` if `fn` does not have a name.@paramoptions Configuration options for the suite. This supports the same options as {@link test}.@paramfn The suite function declaring nested tests and suites. The first argument to this function is a {@link SuiteContext} object.@returnImmediately fulfilled with `undefined`.@sincev20.13.0
describe
('endpoints', { test.TestOptions.concurrency?: number | boolean | undefined
If a number is provided, then that many tests would run in parallel. If truthy, it would run (number of cpu cores - 1) tests in parallel. For subtests, it will be `Infinity` tests in parallel. If falsy, it would only run one test at a time. If unspecified, subtests inherit this value from their parent.
@defaultfalse
concurrency
: true }, () => {
let let agent: anyagent; function beforeEach(fn?: it.HookFn, options?: it.HookOptions): void
This function creates a hook that runs before each test in the current suite. ```js describe('tests', async () => { beforeEach(() => console.log('about to run a test')); it('is a subtest', () => { assert.ok('some relevant assertion here'); }); }); ```
@sincev18.8.0, v16.18.0@paramfn The hook function. If the hook uses callbacks, the callback function is passed as the second argument.@paramoptions Configuration options for the hook.
beforeEach
(() => {
let agent: anyagent = new import MockAgentMockAgent(); import setGlobalDispatchersetGlobalDispatcher(let agent: anyagent); }); function it(name?: string, fn?: it.TestFn): Promise<void> (+3 overloads)
The `test()` function is the value imported from the `test` module. Each invocation of this function results in reporting the test to the `TestsStream`. The `TestContext` object passed to the `fn` argument can be used to perform actions related to the current test. Examples include skipping the test, adding additional diagnostic information, or creating subtests. `test()` returns a `Promise` that fulfills once the test completes. if `test()` is called within a suite, it fulfills immediately. The return value can usually be discarded for top level tests. However, the return value from subtests should be used to prevent the parent test from finishing first and cancelling the subtest as shown in the following example. ```js 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); }); }); }); ``` The `timeout` option can be used to fail the test if it takes longer than `timeout` milliseconds to complete. However, it is not a reliable mechanism for canceling tests because a running test might block the application thread and thus prevent the scheduled cancellation.
@sincev18.0.0, v16.17.0@paramname The name of the test, which is displayed when reporting test results. Defaults to the `name` property of `fn`, or `'<anonymous>'` if `fn` does not have a name.@paramoptions Configuration options for the test.@paramfn The function under test. The first argument to this function is a {@link TestContext } object. If the test uses callbacks, the callback function is passed as the second argument.@returnFulfilled with `undefined` once the test completes, or immediately if the test runs within a suite.
it
('should retrieve data', async () => {
const const endpoint: "foo"endpoint = 'foo'; const const code: 200code = 200; const
const data: {
    key: string;
    val: string;
}
data
= {
key: stringkey: 'good', val: stringval: 'item', }; let agent: anyagent .get('https://example.com') .intercept({ path: stringpath: const endpoint: "foo"endpoint, method: stringmethod: 'GET', }) .reply(const code: 200code,
const data: {
    key: string;
    val: string;
}
data
);
const assert: Omit<typeof assert, "equal" | "notEqual" | "deepEqual" | "notDeepEqual" | "ok" | "strictEqual" | "deepStrictEqual" | "ifError" | "strict" | "AssertionError"> & {
    ...;
}
In strict assertion mode, non-strict methods behave like their corresponding strict methods. For example, {@link deepEqual } will behave like {@link deepStrictEqual } . In strict assertion mode, error messages for objects display a diff. In legacy assertion mode, error messages for objects display the objects, often truncated. To use strict assertion mode: ```js import { strict as assert } from 'node:assert'; import assert from 'node:assert/strict'; ``` Example error diff: ```js import { strict as assert } from 'node:assert'; assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); // AssertionError: Expected inputs to be strictly deep-equal: // + actual - expected ... Lines skipped // // [ // [ // ... // 2, // + 3 // - '3' // ], // ... // 5 // ] ``` To deactivate the colors, use the `NO_COLOR` or `NODE_DISABLE_COLORS` environment variables. This will also deactivate the colors in the REPL. For more on color support in terminal environments, read the tty `getColorDepth()` documentation.
@sincev15.0.0, v13.9.0, v12.16.2, v9.9.0
assert
.
deepEqual: <{
    code: number;
    data: {
        key: string;
        val: string;
    };
}>(actual: unknown, expected: {
    code: number;
    data: {
        key: string;
        val: string;
    };
}, message?: string | Error) => asserts actual is {
    code: number;
    data: {
        key: string;
        val: string;
    };
}
Tests for deep equality between the `actual` and `expected` parameters. "Deep" equality means that the enumerable "own" properties of child objects are recursively evaluated also by the following rules.
@sincev1.2.0
deepEqual
(await import endpointsendpoints.get(const endpoint: "foo"endpoint), {
code: numbercode,
data: {
    key: string;
    val: string;
}
data
,
}); }); function it(name?: string, fn?: it.TestFn): Promise<void> (+3 overloads)
The `test()` function is the value imported from the `test` module. Each invocation of this function results in reporting the test to the `TestsStream`. The `TestContext` object passed to the `fn` argument can be used to perform actions related to the current test. Examples include skipping the test, adding additional diagnostic information, or creating subtests. `test()` returns a `Promise` that fulfills once the test completes. if `test()` is called within a suite, it fulfills immediately. The return value can usually be discarded for top level tests. However, the return value from subtests should be used to prevent the parent test from finishing first and cancelling the subtest as shown in the following example. ```js 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); }); }); }); ``` The `timeout` option can be used to fail the test if it takes longer than `timeout` milliseconds to complete. However, it is not a reliable mechanism for canceling tests because a running test might block the application thread and thus prevent the scheduled cancellation.
@sincev18.0.0, v16.17.0@paramname The name of the test, which is displayed when reporting test results. Defaults to the `name` property of `fn`, or `'<anonymous>'` if `fn` does not have a name.@paramoptions Configuration options for the test.@paramfn The function under test. The first argument to this function is a {@link TestContext } object. If the test uses callbacks, the callback function is passed as the second argument.@returnFulfilled with `undefined` once the test completes, or immediately if the test runs within a suite.
it
('should save data', async () => {
const const endpoint: "foo/1"endpoint = 'foo/1'; const const code: 201code = 201; const
const data: {
    key: string;
    val: string;
}
data
= {
key: stringkey: 'good', val: stringval: 'item', }; let agent: anyagent .get('https://example.com') .intercept({ path: stringpath: const endpoint: "foo/1"endpoint, method: stringmethod: 'PUT', }) .reply(const code: 201code,
const data: {
    key: string;
    val: string;
}
data
);
const assert: Omit<typeof assert, "equal" | "notEqual" | "deepEqual" | "notDeepEqual" | "ok" | "strictEqual" | "deepStrictEqual" | "ifError" | "strict" | "AssertionError"> & {
    ...;
}
In strict assertion mode, non-strict methods behave like their corresponding strict methods. For example, {@link deepEqual } will behave like {@link deepStrictEqual } . In strict assertion mode, error messages for objects display a diff. In legacy assertion mode, error messages for objects display the objects, often truncated. To use strict assertion mode: ```js import { strict as assert } from 'node:assert'; import assert from 'node:assert/strict'; ``` Example error diff: ```js import { strict as assert } from 'node:assert'; assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); // AssertionError: Expected inputs to be strictly deep-equal: // + actual - expected ... Lines skipped // // [ // [ // ... // 2, // + 3 // - '3' // ], // ... // 5 // ] ``` To deactivate the colors, use the `NO_COLOR` or `NODE_DISABLE_COLORS` environment variables. This will also deactivate the colors in the REPL. For more on color support in terminal environments, read the tty `getColorDepth()` documentation.
@sincev15.0.0, v13.9.0, v12.16.2, v9.9.0
assert
.
deepEqual: <{
    code: number;
    data: {
        key: string;
        val: string;
    };
}>(actual: unknown, expected: {
    code: number;
    data: {
        key: string;
        val: string;
    };
}, message?: string | Error) => asserts actual is {
    code: number;
    data: {
        key: string;
        val: string;
    };
}
Tests for deep equality between the `actual` and `expected` parameters. "Deep" equality means that the enumerable "own" properties of child objects are recursively evaluated also by the following rules.
@sincev1.2.0
deepEqual
(await import endpointsendpoints.save(const endpoint: "foo/1"endpoint), {
code: numbercode,
data: {
    key: string;
    val: string;
}
data
,
}); }); });

Time

Like Doctor Strange, you too can control time. You would usually do this just for convenience to avoid artificially protracted test runs (do you really want to wait 3 minutes for that setTimeout() to trigger?). You may also want to travel through time. This leverages mock.timers from the Node.js test runner.

Note the use of time-zone here (Z in the time-stamps). Neglecting to include a consistent time-zone will likely lead to unexpected restults.

import 
const assert: Omit<typeof assert, "equal" | "notEqual" | "deepEqual" | "notDeepEqual" | "ok" | "strictEqual" | "deepStrictEqual" | "ifError" | "strict" | "AssertionError"> & {
    ...;
}
In strict assertion mode, non-strict methods behave like their corresponding strict methods. For example, {@link deepEqual } will behave like {@link deepStrictEqual } . In strict assertion mode, error messages for objects display a diff. In legacy assertion mode, error messages for objects display the objects, often truncated. To use strict assertion mode: ```js import { strict as assert } from 'node:assert'; import assert from 'node:assert/strict'; ``` Example error diff: ```js import { strict as assert } from 'node:assert'; assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); // AssertionError: Expected inputs to be strictly deep-equal: // + actual - expected ... Lines skipped // // [ // [ // ... // 2, // + 3 // - '3' // ], // ... // 5 // ] ``` To deactivate the colors, use the `NO_COLOR` or `NODE_DISABLE_COLORS` environment variables. This will also deactivate the colors in the REPL. For more on color support in terminal environments, read the tty `getColorDepth()` documentation.
@sincev15.0.0, v13.9.0, v12.16.2, v9.9.0
assert
from 'node:assert/strict';
import { function describe(name?: string, options?: it.TestOptions, fn?: it.SuiteFn): Promise<void> (+3 overloads)
The `suite()` function is imported from the `node:test` module.
@paramname The name of the suite, which is displayed when reporting test results. Defaults to the `name` property of `fn`, or `'<anonymous>'` if `fn` does not have a name.@paramoptions Configuration options for the suite. This supports the same options as {@link test}.@paramfn The suite function declaring nested tests and suites. The first argument to this function is a {@link SuiteContext} object.@returnImmediately fulfilled with `undefined`.@sincev20.13.0
describe
, function it(name?: string, fn?: it.TestFn): Promise<void> (+3 overloads)
The `test()` function is the value imported from the `test` module. Each invocation of this function results in reporting the test to the `TestsStream`. The `TestContext` object passed to the `fn` argument can be used to perform actions related to the current test. Examples include skipping the test, adding additional diagnostic information, or creating subtests. `test()` returns a `Promise` that fulfills once the test completes. if `test()` is called within a suite, it fulfills immediately. The return value can usually be discarded for top level tests. However, the return value from subtests should be used to prevent the parent test from finishing first and cancelling the subtest as shown in the following example. ```js 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); }); }); }); ``` The `timeout` option can be used to fail the test if it takes longer than `timeout` milliseconds to complete. However, it is not a reliable mechanism for canceling tests because a running test might block the application thread and thus prevent the scheduled cancellation.
@sincev18.0.0, v16.17.0@paramname The name of the test, which is displayed when reporting test results. Defaults to the `name` property of `fn`, or `'<anonymous>'` if `fn` does not have a name.@paramoptions Configuration options for the test.@paramfn The function under test. The first argument to this function is a {@link TestContext } object. If the test uses callbacks, the callback function is passed as the second argument.@returnFulfilled with `undefined` once the test completes, or immediately if the test runs within a suite.
it
, const mock: it.MockTrackermock } from 'node:test';
import import agoago from './ago.mjs'; function describe(name?: string, options?: it.TestOptions, fn?: it.SuiteFn): Promise<void> (+3 overloads)
The `suite()` function is imported from the `node:test` module.
@paramname The name of the suite, which is displayed when reporting test results. Defaults to the `name` property of `fn`, or `'<anonymous>'` if `fn` does not have a name.@paramoptions Configuration options for the suite. This supports the same options as {@link test}.@paramfn The suite function declaring nested tests and suites. The first argument to this function is a {@link SuiteContext} object.@returnImmediately fulfilled with `undefined`.@sincev20.13.0
describe
('whatever', { test.TestOptions.concurrency?: number | boolean | undefined
If a number is provided, then that many tests would run in parallel. If truthy, it would run (number of cpu cores - 1) tests in parallel. For subtests, it will be `Infinity` tests in parallel. If falsy, it would only run one test at a time. If unspecified, subtests inherit this value from their parent.
@defaultfalse
concurrency
: true }, () => {
function it(name?: string, fn?: it.TestFn): Promise<void> (+3 overloads)
The `test()` function is the value imported from the `test` module. Each invocation of this function results in reporting the test to the `TestsStream`. The `TestContext` object passed to the `fn` argument can be used to perform actions related to the current test. Examples include skipping the test, adding additional diagnostic information, or creating subtests. `test()` returns a `Promise` that fulfills once the test completes. if `test()` is called within a suite, it fulfills immediately. The return value can usually be discarded for top level tests. However, the return value from subtests should be used to prevent the parent test from finishing first and cancelling the subtest as shown in the following example. ```js 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); }); }); }); ``` The `timeout` option can be used to fail the test if it takes longer than `timeout` milliseconds to complete. However, it is not a reliable mechanism for canceling tests because a running test might block the application thread and thus prevent the scheduled cancellation.
@sincev18.0.0, v16.17.0@paramname The name of the test, which is displayed when reporting test results. Defaults to the `name` property of `fn`, or `'<anonymous>'` if `fn` does not have a name.@paramoptions Configuration options for the test.@paramfn The function under test. The first argument to this function is a {@link TestContext } object. If the test uses callbacks, the callback function is passed as the second argument.@returnFulfilled with `undefined` once the test completes, or immediately if the test runs within a suite.
it
('should choose "minutes" when that\'s the closet unit', () => {
const mock: it.MockTrackermock.test.MockTracker.timers: it.MockTimerstimers.test.MockTimers.enable(options?: it.MockTimersOptions): void
Enables timer mocking for the specified timers. **Note:** When you enable mocking for a specific timer, its associated clear function will also be implicitly mocked. **Note:** Mocking `Date` will affect the behavior of the mocked timers as they use the same internal clock. Example usage without setting initial time: ```js import { mock } from 'node:test'; mock.timers.enable({ apis: ['setInterval', 'Date'], now: 1234 }); ``` The above example enables mocking for the `Date` constructor, `setInterval` timer and implicitly mocks the `clearInterval` function. Only the `Date` constructor from `globalThis`, `setInterval` and `clearInterval` functions from `node:timers`, `node:timers/promises`, and `globalThis` will be mocked. Example usage with initial time set ```js import { mock } from 'node:test'; mock.timers.enable({ apis: ['Date'], now: 1000 }); ``` Example usage with initial Date object as time set ```js import { mock } from 'node:test'; mock.timers.enable({ apis: ['Date'], now: new Date() }); ``` Alternatively, if you call `mock.timers.enable()` without any parameters: All timers (`'setInterval'`, `'clearInterval'`, `'Date'`, `'setImmediate'`, `'clearImmediate'`, `'setTimeout'`, and `'clearTimeout'`) will be mocked. The `setInterval`, `clearInterval`, `setTimeout`, and `clearTimeout` functions from `node:timers`, `node:timers/promises`, and `globalThis` will be mocked. The `Date` constructor from `globalThis` will be mocked. If there is no initial epoch set, the initial date will be based on 0 in the Unix epoch. This is `January 1st, 1970, 00:00:00 UTC`. You can set an initial date by passing a now property to the `.enable()` method. This value will be used as the initial date for the mocked Date object. It can either be a positive integer, or another Date object.
@sincev20.4.0
enable
({ test.MockTimersOptions.now?: number | Date | undefinednow: new
var Date: DateConstructor
new (value: number | string | Date) => Date (+4 overloads)
Date
('2000-01-01T00:02:02Z') });
const const t: anyt = import agoago('1999-12-01T23:59:59Z');
const assert: Omit<typeof assert, "equal" | "notEqual" | "deepEqual" | "notDeepEqual" | "ok" | "strictEqual" | "deepStrictEqual" | "ifError" | "strict" | "AssertionError"> & {
    ...;
}
In strict assertion mode, non-strict methods behave like their corresponding strict methods. For example, {@link deepEqual } will behave like {@link deepStrictEqual } . In strict assertion mode, error messages for objects display a diff. In legacy assertion mode, error messages for objects display the objects, often truncated. To use strict assertion mode: ```js import { strict as assert } from 'node:assert'; import assert from 'node:assert/strict'; ``` Example error diff: ```js import { strict as assert } from 'node:assert'; assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); // AssertionError: Expected inputs to be strictly deep-equal: // + actual - expected ... Lines skipped // // [ // [ // ... // 2, // + 3 // - '3' // ], // ... // 5 // ] ``` To deactivate the colors, use the `NO_COLOR` or `NODE_DISABLE_COLORS` environment variables. This will also deactivate the colors in the REPL. For more on color support in terminal environments, read the tty `getColorDepth()` documentation.
@sincev15.0.0, v13.9.0, v12.16.2, v9.9.0
assert
.equal: <"2 minutes ago">(actual: unknown, expected: "2 minutes ago", message?: string | Error) => asserts actual is "2 minutes ago"
Tests strict equality between the `actual` and `expected` parameters as determined by [`Object.is()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). ```js import assert from 'node:assert/strict'; assert.strictEqual(1, 2); // AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal: // // 1 !== 2 assert.strictEqual(1, 1); // OK assert.strictEqual('Hello foobar', 'Hello World!'); // AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal: // + actual - expected // // + 'Hello foobar' // - 'Hello World!' // ^ const apples = 1; const oranges = 2; assert.strictEqual(apples, oranges, `apples ${apples} !== oranges ${oranges}`); // AssertionError [ERR_ASSERTION]: apples 1 !== oranges 2 assert.strictEqual(1, '1', new TypeError('Inputs are not identical')); // TypeError: Inputs are not identical ``` If the values are not strictly equal, an `AssertionError` is thrown with a `message` property set equal to the value of the `message` parameter. If the `message` parameter is undefined, a default error message is assigned. If the `message` parameter is an instance of an `Error` then it will be thrown instead of the `AssertionError`.
@sincev0.1.21
equal
(const t: anyt, '2 minutes ago');
}); });

This is especially useful when comparing against a static fixture (that is checked into a repository), such as in snapshot testing.