From 719ff4732ede1a6e4bf12a66a81a258df64b9b0c Mon Sep 17 00:00:00 2001 From: Seong Min Park Date: Wed, 30 Jul 2025 10:33:45 +0900 Subject: [PATCH 1/2] feat: add mockThrowValue and mockThrowValueOnce methods to jest.fn() --- docs/MockFunctionAPI.md | 70 +++++++++++++++++++ .../__typetests__/mock-functions.test.ts | 22 ++++++ .../jest-mock/src/__tests__/index.test.ts | 19 +++++ packages/jest-mock/src/index.ts | 12 ++++ .../version-30.0/MockFunctionAPI.md | 70 +++++++++++++++++++ 5 files changed, 193 insertions(+) diff --git a/docs/MockFunctionAPI.md b/docs/MockFunctionAPI.md index 42dbf4401ec8..af1ccc3f7b65 100644 --- a/docs/MockFunctionAPI.md +++ b/docs/MockFunctionAPI.md @@ -513,6 +513,76 @@ test('async test', async () => { }); ``` +### `mockFn.mockThrowValue(value)` + +Shorthand for: + +```js +jest.fn().mockImplementation(() => { + throw value; +}); +``` + +Useful to create mock functions that will always throw: + +```js tab +test('sync test', () => { + const mockFn = jest.fn().mockThrowValue(new Error('Sync error message')); + + expect(() => mockFn()).toThrow('Sync error message'); +}); +``` + +```ts tab +import {jest, test} from '@jest/globals'; + +test('sync test', () => { + const mockFn = jest + .fn<() => never>() + .mockThrowValue(new Error('Sync error message')); + + expect(() => mockFn()).toThrow('Sync error message'); +}); +``` + +### `mockFn.mockThrowValueOnce(value)` + +Shorthand for: + +```js +jest.fn().mockImplementationOnce(() => { + throw value; +}); +``` + +Useful together with `.mockReturnValueOnce()` or to throw different exceptions over multiple calls: + +```js tab +test('sync test', () => { + const mockFn = jest + .fn() + .mockReturnValueOnce('first call') + .mockThrowValueOnce(new Error('Sync error message')); + + expect(mockFn()).toBe('first call'); + expect(() => mockFn()).toThrow('Sync error message'); +}); +``` + +```ts tab +import {jest, test} from '@jest/globals'; + +test('sync test', () => { + const mockFn = jest + .fn<() => string>() + .mockReturnValueOnce('first call') + .mockThrowValueOnce(new Error('Sync error message')); + + expect(mockFn()).toBe('first call'); + expect(() => mockFn()).toThrow('Sync error message'); +}); +``` + ### `mockFn.withImplementation(fn, callback)` Accepts a function which should be temporarily used as the implementation of the mock while the callback is being executed. diff --git a/packages/jest-mock/__typetests__/mock-functions.test.ts b/packages/jest-mock/__typetests__/mock-functions.test.ts index 2121133eadb5..1155d4196994 100644 --- a/packages/jest-mock/__typetests__/mock-functions.test.ts +++ b/packages/jest-mock/__typetests__/mock-functions.test.ts @@ -361,6 +361,28 @@ describe('jest.fn()', () => { ).type.not.toBeCallableWith(); }); + test('.mockThrowValue()', () => { + expect( + fn(() => 'string').mockThrowValue(new Error('Mock error')), + ).type.toBe string>>(); + expect(fn(() => 'string').mockThrowValue('Mock error')).type.toBe< + Mock<() => string> + >(); + + expect(fn(() => 'string').mockThrowValue).type.not.toBeCallableWith(); + }); + + test('.mockThrowValueOnce()', () => { + expect( + fn(() => 'string').mockThrowValueOnce(new Error('Mock error')), + ).type.toBe string>>(); + expect(fn(() => 'string').mockThrowValueOnce('Mock error')).type.toBe< + Mock<() => string> + >(); + + expect(fn(() => 'string').mockThrowValueOnce).type.not.toBeCallableWith(); + }); + test('.withImplementation()', () => { expect(mockFn.withImplementation(mockFnImpl, () => {})).type.toBe(); expect(mockFn.withImplementation(mockFnImpl, async () => {})).type.toBe< diff --git a/packages/jest-mock/src/__tests__/index.test.ts b/packages/jest-mock/src/__tests__/index.test.ts index 0b03678c4040..189e8b84eee8 100644 --- a/packages/jest-mock/src/__tests__/index.test.ts +++ b/packages/jest-mock/src/__tests__/index.test.ts @@ -729,6 +729,25 @@ describe('moduleMocker', () => { ]); }); + it('supports mocking functions that throw', () => { + const err = new Error('thrown'); + const fn = moduleMocker.fn(); + fn.mockThrowValue(err); + + expect(() => fn()).toThrow(err); + }); + + it('supports mocking functions that throw only once', () => { + const defaultErr = new Error('default thrown'); + const err = new Error('thrown'); + const fn = moduleMocker.fn(); + fn.mockThrowValue(defaultErr); + fn.mockThrowValueOnce(err); + + expect(() => fn()).toThrow(err); + expect(() => fn()).toThrow(defaultErr); + }); + describe('return values', () => { it('tracks return values', () => { const fn = moduleMocker.fn(x => x * 2); diff --git a/packages/jest-mock/src/index.ts b/packages/jest-mock/src/index.ts index b9582dbf8734..eafc47df2f7a 100644 --- a/packages/jest-mock/src/index.ts +++ b/packages/jest-mock/src/index.ts @@ -155,6 +155,8 @@ export interface MockInstance mockResolvedValueOnce(value: ResolveType): this; mockRejectedValue(value: RejectType): this; mockRejectedValueOnce(value: RejectType): this; + mockThrowValue(value: unknown): this; + mockThrowValueOnce(value: unknown): this; } export interface Replaced { @@ -791,6 +793,16 @@ export class ModuleMocker { this._environmentGlobal.Promise.reject(value), ); + f.mockThrowValue = (value: unknown) => + f.mockImplementation(() => { + throw value; + }); + + f.mockThrowValueOnce = (value: unknown) => + f.mockImplementationOnce(() => { + throw value; + }); + f.mockImplementationOnce = (fn: T) => { // next function call will use this mock implementation return value // or default mock implementation return value diff --git a/website/versioned_docs/version-30.0/MockFunctionAPI.md b/website/versioned_docs/version-30.0/MockFunctionAPI.md index 42dbf4401ec8..af1ccc3f7b65 100644 --- a/website/versioned_docs/version-30.0/MockFunctionAPI.md +++ b/website/versioned_docs/version-30.0/MockFunctionAPI.md @@ -513,6 +513,76 @@ test('async test', async () => { }); ``` +### `mockFn.mockThrowValue(value)` + +Shorthand for: + +```js +jest.fn().mockImplementation(() => { + throw value; +}); +``` + +Useful to create mock functions that will always throw: + +```js tab +test('sync test', () => { + const mockFn = jest.fn().mockThrowValue(new Error('Sync error message')); + + expect(() => mockFn()).toThrow('Sync error message'); +}); +``` + +```ts tab +import {jest, test} from '@jest/globals'; + +test('sync test', () => { + const mockFn = jest + .fn<() => never>() + .mockThrowValue(new Error('Sync error message')); + + expect(() => mockFn()).toThrow('Sync error message'); +}); +``` + +### `mockFn.mockThrowValueOnce(value)` + +Shorthand for: + +```js +jest.fn().mockImplementationOnce(() => { + throw value; +}); +``` + +Useful together with `.mockReturnValueOnce()` or to throw different exceptions over multiple calls: + +```js tab +test('sync test', () => { + const mockFn = jest + .fn() + .mockReturnValueOnce('first call') + .mockThrowValueOnce(new Error('Sync error message')); + + expect(mockFn()).toBe('first call'); + expect(() => mockFn()).toThrow('Sync error message'); +}); +``` + +```ts tab +import {jest, test} from '@jest/globals'; + +test('sync test', () => { + const mockFn = jest + .fn<() => string>() + .mockReturnValueOnce('first call') + .mockThrowValueOnce(new Error('Sync error message')); + + expect(mockFn()).toBe('first call'); + expect(() => mockFn()).toThrow('Sync error message'); +}); +``` + ### `mockFn.withImplementation(fn, callback)` Accepts a function which should be temporarily used as the implementation of the mock while the callback is being executed. From e1df5fd999daeb84f974953f4b97dabb0d500761 Mon Sep 17 00:00:00 2001 From: Seong Min Park Date: Wed, 30 Jul 2025 10:50:23 +0900 Subject: [PATCH 2/2] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0179da15bd8..0770ecfcf2a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## main +### Features + +- `[jest-mock]` Add `mockThrowValue` and `mockThrowValueOnce` methods to `jest.fn()` for synchronous error throwing + ## 30.0.5 ### Features