Skip to content

Commit

Permalink
test_runner: support module mocking
Browse files Browse the repository at this point in the history
This commit adds experimental module mocking to the test runner.

PR-URL: #52848
Fixes: #51164
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
  • Loading branch information
cjihrig committed May 19, 2024
1 parent cb90a31 commit a619789
Show file tree
Hide file tree
Showing 14 changed files with 1,336 additions and 12 deletions.
10 changes: 10 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,16 @@ generated as part of the test runner output. If no tests are run, a coverage
report is not generated. See the documentation on
[collecting code coverage from tests][] for more details.

### `--experimental-test-module-mocks`

<!-- YAML
added: REPLACEME
-->

> Stability: 1.0 - Early development
Enable module mocking in the test runner.

### `--experimental-vm-modules`

<!-- YAML
Expand Down
81 changes: 81 additions & 0 deletions doc/api/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -1775,6 +1775,25 @@ added:
Resets the implementation of the mock function to its original behavior. The
mock can still be used after calling this function.

## Class: `MockModuleContext`

<!-- YAML
added: REPLACEME
-->

> Stability: 1.0 - Early development
The `MockModuleContext` class is used to manipulate the behavior of module mocks
created via the [`MockTracker`][] APIs.

### `ctx.restore()`

<!-- YAML
added: REPLACEME
-->

Resets the implementation of the mock module.

## Class: `MockTracker`

<!-- YAML
Expand Down Expand Up @@ -1908,6 +1927,68 @@ test('spies on an object method', (t) => {
});
```

### `mock.module(specifier[, options])`

<!-- YAML
added: REPLACEME
-->

> Stability: 1.0 - Early development
* `specifier` {string} A string identifying the module to mock.
* `options` {Object} Optional configuration options for the mock module. The
following properties are supported:
* `cache` {boolean} If `false`, each call to `require()` or `import()`
generates a new mock module. If `true`, subsequent calls will return the same
module mock, and the mock module is inserted into the CommonJS cache.
**Default:** false.
* `defaultExport` {any} An optional value used 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`.
* `namedExports` {Object} An optional 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.
* Returns: {MockModuleContext} An object that can be used to manipulate the mock.

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. The following example demonstrates how a mock
is created for a module.

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

### `mock.reset()`

<!-- YAML
Expand Down
3 changes: 3 additions & 0 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ Use this flag to enable ShadowRealm support.
.It Fl -experimental-test-coverage
Enable code coverage in the test runner.
.
.It Fl -experimental-test-module-mocks
Enable module mocking in the test runner.
.
.It Fl -experimental-eventsource
Enable experimental support for the EventSource Web API.
.
Expand Down

0 comments on commit a619789

Please sign in to comment.