Commit f513ec7e7ca for nodejs
commit f513ec7e7cad019368f26528041e9684ab12fba6
Author: Guilherme Araújo <arauujogui@gmail.com>
Date: Fri Oct 16 15:06:44 2026 -0300
test_runner: add mockReturnValue and promise helpers
Add mockReturnValue(), mockResolvedValue() and mockRejectedValue()
to MockFunctionContext as shorthands for common mockImplementation()
patterns.
Signed-off-by: Guilherme Araújo <arauujogui@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66631
Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
diff --git a/doc/api/test.md b/doc/api/test.md
index 4df041dbdfb..48d5b0c004c 100644
--- a/doc/api/test.md
+++ b/doc/api/test.md
@@ -2466,6 +2466,89 @@ test('changes a mock behavior once', (t) => {
});
```
+### `ctx.mockRejectedValue(reason)`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* `reason` {any} The value the returned promise rejects with.
+
+Changes the mock's implementation to a function that returns a promise rejected
+with `reason`. This is shorthand for
+`ctx.mockImplementation(() => Promise.reject(reason))`.
+
+```js
+test('mocks a rejected value', async (t) => {
+ const fn = t.mock.fn();
+
+ fn.mock.mockRejectedValue(new Error('boom'));
+ await assert.rejects(fn(), { message: 'boom' });
+});
+```
+
+### `ctx.mockResolvedValue(value)`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* `value` {any} The value the returned promise resolves with.
+
+Changes the mock's implementation to a function that returns a promise resolved
+with `value`. This is shorthand for
+`ctx.mockImplementation(() => Promise.resolve(value))`.
+
+```js
+test('mocks a resolved value', async (t) => {
+ const fn = t.mock.fn();
+
+ fn.mock.mockResolvedValue(42);
+ assert.strictEqual(await fn(), 42);
+});
+```
+
+### `ctx.mockReturnValue(value)`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* `value` {any} The value returned by the mock.
+
+Changes the mock's implementation to a function that returns `value`. This is
+shorthand for `ctx.mockImplementation(() => value)`.
+
+```js
+test('mocks a return value', (t) => {
+ const fn = t.mock.fn();
+
+ fn.mock.mockReturnValue(42);
+ assert.strictEqual(fn(), 42);
+ assert.strictEqual(fn(), 42);
+});
+```
+
+### `ctx.mockThrownValue(value)`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* `value` {any} The value thrown by the mock.
+
+Changes the mock's implementation to a function that throws `value`. This is
+shorthand for `ctx.mockImplementation(() => { throw value; })`.
+
+```js
+test('mocks a thrown value', (t) => {
+ const fn = t.mock.fn();
+
+ fn.mock.mockThrownValue(new Error('boom'));
+ assert.throws(fn, { message: 'boom' });
+});
+```
+
### `ctx.resetCalls()`
<!-- YAML
diff --git a/lib/internal/test_runner/mock/mock.js b/lib/internal/test_runner/mock/mock.js
index 996c8a7bcca..4861f31b5f2 100644
--- a/lib/internal/test_runner/mock/mock.js
+++ b/lib/internal/test_runner/mock/mock.js
@@ -15,6 +15,8 @@ const {
ObjectKeys,
ObjectSetPrototypeOf,
ObjectValues,
+ PromiseReject,
+ PromiseResolve,
Proxy,
ReflectApply,
ReflectConstruct,
@@ -143,6 +145,38 @@ class MockFunctionContext {
this.#mocks.set(call, implementation);
}
+ /**
+ * Sets a new implementation that always returns the given value.
+ * @param {any} value - The value to return.
+ */
+ mockReturnValue(value) {
+ this.#implementation = function() { return value; };
+ }
+
+ /**
+ * Sets a new implementation that returns a promise resolved with the given value.
+ * @param {any} value - The value to resolve with.
+ */
+ mockResolvedValue(value) {
+ this.#implementation = function() { return PromiseResolve(value); };
+ }
+
+ /**
+ * Sets a new implementation that returns a promise rejected with the given reason.
+ * @param {any} reason - The reason to reject with.
+ */
+ mockRejectedValue(reason) {
+ this.#implementation = function() { return PromiseReject(reason); };
+ }
+
+ /**
+ * Sets a new implementation that always throws the given value.
+ * @param {any} value - The value to throw.
+ */
+ mockThrownValue(value) {
+ this.#implementation = function() { throw value; };
+ }
+
/**
* Restores the original function that was mocked.
*/
diff --git a/test/parallel/test-runner-mocking.js b/test/parallel/test-runner-mocking.js
index 634eff93165..59a919579e7 100644
--- a/test/parallel/test-runner-mocking.js
+++ b/test/parallel/test-runner-mocking.js
@@ -892,6 +892,52 @@ test('mock implementation can be changed dynamically', (t) => {
assert.strictEqual(fn.mock.callCount(), 12);
});
+test('mockReturnValue() sets a fixed return value', (t) => {
+ const fn = t.mock.fn(common.mustNotCall());
+
+ fn.mock.mockReturnValue(42);
+ assert.strictEqual(fn(), 42);
+ assert.strictEqual(fn(1, 2), 42);
+ assert.strictEqual(fn.mock.callCount(), 2);
+ assert.strictEqual(fn.mock.calls[1].result, 42);
+});
+
+test('mockResolvedValue() returns a resolved promise', async (t) => {
+ const fn = t.mock.fn(common.mustNotCall());
+
+ fn.mock.mockResolvedValue(42);
+ const result = fn();
+ assert(result instanceof Promise);
+ assert.strictEqual(await result, 42);
+});
+
+test('mockRejectedValue() returns a rejected promise', async (t) => {
+ const fn = t.mock.fn(common.mustNotCall());
+ const error = new Error('boom');
+
+ fn.mock.mockRejectedValue(error);
+ await assert.rejects(fn(), error);
+});
+
+test('mockThrownValue() throws the given value', (t) => {
+ const fn = t.mock.fn(common.mustNotCall());
+ const error = new Error('boom');
+
+ fn.mock.mockThrownValue(error);
+ assert.throws(fn, error);
+ assert.strictEqual(fn.mock.callCount(), 1);
+ assert.strictEqual(fn.mock.calls[0].error, error);
+});
+
+test('mockImplementationOnce() takes precedence over mockReturnValue()', (t) => {
+ const fn = t.mock.fn();
+
+ fn.mock.mockReturnValue(1);
+ fn.mock.mockImplementationOnce(() => 2);
+ assert.strictEqual(fn(), 2);
+ assert.strictEqual(fn(), 1);
+});
+
test('local mocks are auto restored after the test finishes', async (t) => {
const obj = {
foo() {},