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:
type | description | example | mock candidates |
---|---|---|---|
unit | the smallest bit of code you can isolate | const sum = (a, b) => a + b | own code, external code, external system |
component | a unit + dependencies | const arithmetic = (op = sum, a, b) => ops[op](a, b) | external code, external system |
integration | components fitting together | - | external code, external system |
end-to-end (e2e) | app + external data stores, delivery, etc | A 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 foo
foo from './foo.mjs';
export function function main(): void
main() {
const const f: any
f = import foo
foo();
}
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 bar
bar from 'bar';
export function function main(): void
main() {
const const f: any
f = import bar
bar();
}
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 db
db } from 'db';
export function function read(key: any, all?: boolean): any
read(key: any
key, all: boolean
all = false) {
validate(key: any
key, val);
if (all: boolean
all) {
return import db
db.getAll(key: any
key);
}
return import db
db.getOne(key: any
key);
}
export function function save(key: any, val: any): any
save(key: any
key, val: any
val) {
validate(key: any
key, val: any
val);
return import db
db.upsert(key: any
key, val: any
val);
}
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.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');
});
});
```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.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.it, const mock: it.MockTracker
mock } 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.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.concurrency: true }, () => {
const const barMock: it.Mock<(...args: any[]) => undefined>
barMock = const mock: it.MockTracker
mock.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);
});
```fn();
let let foo: any
foo;
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');
});
});
```before(async () => {
const const barNamedExports: any
barNamedExports = 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.then(({ default: _: any
_, ...rest: any
rest }) => rest: any
rest);
// It's usually not necessary to manually call restore() after each
// nor reset() after all (node does this automatically).
const mock: it.MockTracker
mock.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.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: any
barNamedExports,
});
// 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: any
foo } = 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.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);
});
```mockImplementationOnce(function function (local function) bar_mock(): undefined
bar_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.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`.equal(let foo: any
foo(), 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.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');
});
});
```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.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.it } from 'node:test';
import { import MockAgent
MockAgent, import setGlobalDispatcher
setGlobalDispatcher } from 'undici';
import import endpoints
endpoints 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.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.concurrency: true }, () => {
let let agent: any
agent;
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');
});
});
```beforeEach(() => {
let agent: any
agent = new import MockAgent
MockAgent();
import setGlobalDispatcher
setGlobalDispatcher(let agent: any
agent);
});
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.it('should retrieve data', async () => {
const const endpoint: "foo"
endpoint = 'foo';
const const code: 200
code = 200;
const const data: {
key: string;
val: string;
}
data = {
key: string
key: 'good',
val: string
val: 'item',
};
let agent: any
agent
.get('https://example.com')
.intercept({
path: string
path: const endpoint: "foo"
endpoint,
method: string
method: 'GET',
})
.reply(const code: 200
code, 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.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.deepEqual(await import endpoints
endpoints.get(const endpoint: "foo"
endpoint), {
code: number
code,
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.it('should save data', async () => {
const const endpoint: "foo/1"
endpoint = 'foo/1';
const const code: 201
code = 201;
const const data: {
key: string;
val: string;
}
data = {
key: string
key: 'good',
val: string
val: 'item',
};
let agent: any
agent
.get('https://example.com')
.intercept({
path: string
path: const endpoint: "foo/1"
endpoint,
method: string
method: 'PUT',
})
.reply(const code: 201
code, 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.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.deepEqual(await import endpoints
endpoints.save(const endpoint: "foo/1"
endpoint), {
code: number
code,
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.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.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.it, const mock: it.MockTracker
mock } from 'node:test';
import import ago
ago 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.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.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.it('should choose "minutes" when that\'s the closet unit', () => {
const mock: it.MockTracker
mock.test.MockTracker.timers: it.MockTimers
timers.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.enable({ test.MockTimersOptions.now?: number | Date | undefined
now: new var Date: DateConstructor
new (value: number | string | Date) => Date (+4 overloads)
Date('2000-01-01T00:02:02Z') });
const const t: any
t = import ago
ago('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.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`.equal(const t: any
t, '2 minutes ago');
});
});
This is especially useful when comparing against a static fixture (that is checked into a repository), such as in snapshot testing.