diff --git a/docs/api/mock.md b/docs/api/mock.md
index 4b9e6aefb328..b1fbce45cd47 100644
--- a/docs/api/mock.md
+++ b/docs/api/mock.md
@@ -418,6 +418,40 @@ const myMockFn = vi
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn())
```
+## mockThrow 4.1.0 {#mockthrow}
+
+```ts
+function mockThrow(value: unknown): Mock
+```
+
+Accepts a value that will be thrown whenever the mock function is called.
+
+```ts
+const myMockFn = vi.fn()
+myMockFn.mockThrow(new Error('error message'))
+myMockFn() // throws Error<'error message'>
+```
+
+## mockThrowOnce 4.1.0 {#mockthrowonce}
+
+```ts
+function mockThrowOnce(value: unknown): Mock
+```
+
+Accepts a value that will be thrown during the next function call. If chained, every consecutive call will throw the specified value.
+
+```ts
+const myMockFn = vi
+ .fn()
+ .mockReturnValue('default')
+ .mockThrowOnce(new Error('first call error'))
+ .mockThrowOnce('second call error')
+
+expect(() => myMockFn()).toThrow('first call error')
+expect(() => myMockFn()).toThrow('second call error')
+expect(myMockFn()).toEqual('default')
+```
+
## mock.calls
```ts
diff --git a/packages/spy/src/index.ts b/packages/spy/src/index.ts
index 0b20accc9803..80a1e48bd6b0 100644
--- a/packages/spy/src/index.ts
+++ b/packages/spy/src/index.ts
@@ -140,6 +140,20 @@ export function createMockInstance(options: MockInstanceOption = {}): Mock e
* console.log(myMockFn(), myMockFn(), myMockFn())
*/
mockReturnValueOnce(value: MockReturnType): this
+ /**
+ * Accepts a value that will be thrown whenever the mock function is called.
+ * @see https://vitest.dev/api/mock#mockthrow
+ * @example
+ * const myMockFn = vi.fn().mockThrow(new Error('error'))
+ * myMockFn() // throws 'error'
+ */
+ mockThrow(value: unknown): this
+ /**
+ * Accepts a value that will be thrown during the next function call. If chained, every consecutive call will throw the specified value.
+ * @example
+ * const myMockFn = vi
+ * .fn()
+ * .mockReturnValue('default')
+ * .mockThrowOnce(new Error('first call error'))
+ * .mockThrowOnce('second call error')
+ *
+ * expect(() => myMockFn()).toThrowError('first call error')
+ * expect(() => myMockFn()).toThrowError('second call error')
+ * expect(myMockFn()).toEqual('default')
+ */
+ mockThrowOnce(value: unknown): this
/**
* Accepts a value that will be resolved when the async function is called. TypeScript will only accept values that match the return type of the original function.
* @example
diff --git a/test/core/test/mocking/vi-fn.test.ts b/test/core/test/mocking/vi-fn.test.ts
index f3269ee3d637..71e3d5e8f4af 100644
--- a/test/core/test/mocking/vi-fn.test.ts
+++ b/test/core/test/mocking/vi-fn.test.ts
@@ -519,6 +519,88 @@ describe('vi.fn() implementations', () => {
expect(mock()).toBe(undefined)
})
+ test('vi.fn() with mockThrow', async () => {
+ const mock = vi.fn()
+ mock.mockThrow(new Error('error'))
+ expect(() => mock()).toThrow('error')
+ expect(() => mock()).toThrow('error')
+ expect(() => mock()).toThrow('error')
+ mock.mockReset()
+ expect(mock()).toBe(undefined)
+ })
+
+ test('vi.fn(class) with mockThrow', async () => {
+ const Mock = vi.fn(class {})
+ Mock.mockThrow(new Error('error'))
+ expect(() => new Mock()).toThrow('error')
+ expect(() => new Mock()).toThrow('error')
+ expect(() => new Mock()).toThrow('error')
+ Mock.mockReset()
+ expect(new Mock()).toBeInstanceOf(Mock)
+ })
+
+ test('vi.fn() with mockThrow overriding original mock', async () => {
+ const mock = vi.fn(() => 42)
+ mock.mockThrow(new Error('error'))
+ expect(() => mock()).toThrow('error')
+ expect(() => mock()).toThrow('error')
+ expect(() => mock()).toThrow('error')
+ mock.mockReset()
+ expect(mock()).toBe(42)
+ })
+
+ test('vi.fn() with mockThrow overriding another mock', async () => {
+ const mock = vi.fn().mockImplementation(() => 42)
+ mock.mockThrow(new Error('error'))
+ expect(() => mock()).toThrow('error')
+ expect(() => mock()).toThrow('error')
+ expect(() => mock()).toThrow('error')
+ mock.mockReset()
+ expect(mock()).toBe(undefined)
+ })
+
+ test('vi.fn() with mockThrowOnce', async () => {
+ const mock = vi.fn()
+ mock.mockThrowOnce(new Error('error'))
+ expect(() => mock()).toThrow('error')
+ expect(mock()).toBe(undefined)
+ expect(mock()).toBe(undefined)
+ mock.mockThrowOnce(new Error('error'))
+ mock.mockReset()
+ expect(mock()).toBe(undefined)
+ })
+
+ test('vi.fn(class) with mockThrowOnce', async () => {
+ const Mock = vi.fn(class {})
+ Mock.mockThrowOnce(new Error('error'))
+ expect(() => new Mock()).toThrow('error')
+ expect(new Mock()).toBeInstanceOf(Mock)
+ expect(new Mock()).toBeInstanceOf(Mock)
+ Mock.mockThrowOnce(new Error('error'))
+ Mock.mockReset()
+ expect(new Mock()).toBeInstanceOf(Mock)
+ })
+
+ test('vi.fn() with mockThrowOnce overriding original mock', async () => {
+ const mock = vi.fn(() => 42)
+ mock.mockThrowOnce(new Error('error'))
+ expect(() => mock()).toThrow('error')
+ expect(mock()).toBe(42)
+ expect(mock()).toBe(42)
+ mock.mockReset()
+ expect(mock()).toBe(42)
+ })
+
+ test('vi.fn() with mockThrowOnce overriding another mock', async () => {
+ const mock = vi.fn().mockImplementation(() => 42)
+ mock.mockThrowOnce(new Error('error'))
+ expect(() => mock()).toThrow('error')
+ expect(mock()).toBe(42)
+ expect(mock()).toBe(42)
+ mock.mockReset()
+ expect(mock()).toBe(undefined)
+ })
+
test('vi.fn() with mockResolvedValue', async () => {
const mock = vi.fn()
mock.mockResolvedValue(42)