diff --git a/docs/api/vi.md b/docs/api/vi.md
index d3cc13a19..1a4772dfd 100644
--- a/docs/api/vi.md
+++ b/docs/api/vi.md
@@ -390,6 +390,33 @@ expect(res).toBe(5)
expect(getApples).toHaveNthReturnedWith(2, 5)
```
+### vi.mockObject 3.2.0
+
+- **Type:** `(value: T) => MaybeMockedDeep`
+
+Deeply mocks properties and methods of a given object in the same way as `vi.mock()` mocks module exports. See [automocking](/guide/mocking.html#automocking-algorithm) for the detail.
+
+```ts
+const original = {
+ simple: () => 'value',
+ nested: {
+ method: () => 'real'
+ },
+ prop: 'foo',
+}
+
+const mocked = vi.mockObject(original)
+expect(mocked.simple()).toBe(undefined)
+expect(mocked.nested.method()).toBe(undefined)
+expect(mocked.prop).toBe('foo')
+
+mocked.simple.mockReturnValue('mocked')
+mocked.nested.method.mockReturnValue('mocked nested')
+
+expect(mocked.simple()).toBe('mocked')
+expect(mocked.nested.method()).toBe('mocked nested')
+```
+
### vi.isMockFunction
- **Type:** `(fn: Function) => boolean`
diff --git a/packages/vitest/src/integrations/vi.ts b/packages/vitest/src/integrations/vi.ts
index 638cc5a4d..b2aa5b143 100644
--- a/packages/vitest/src/integrations/vi.ts
+++ b/packages/vitest/src/integrations/vi.ts
@@ -268,6 +268,37 @@ export interface VitestUtils {
path: string
) => Promise>
+ /**
+ * Deeply mocks properties and methods of a given object
+ * in the same way as `vi.mock()` mocks module exports.
+ *
+ * @example
+ * ```ts
+ * const original = {
+ * simple: () => 'value',
+ * nested: {
+ * method: () => 'real'
+ * },
+ * prop: 'foo',
+ * }
+ *
+ * const mocked = vi.mockObject(original)
+ * expect(mocked.simple()).toBe(undefined)
+ * expect(mocked.nested.method()).toBe(undefined)
+ * expect(mocked.prop).toBe('foo')
+ *
+ * mocked.simple.mockReturnValue('mocked')
+ * mocked.nested.method.mockReturnValue('mocked nested')
+ *
+ * expect(mocked.simple()).toBe('mocked')
+ * expect(mocked.nested.method()).toBe('mocked nested')
+ * ```
+ *
+ * @param value - The object to be mocked
+ * @returns A deeply mocked version of the input object
+ */
+ mockObject: (value: T) => MaybeMockedDeep
+
/**
* Type helper for TypeScript. Just returns the object that was passed.
*
@@ -606,6 +637,10 @@ function createVitest(): VitestUtils {
return _mocker().importMock(path, getImporter('importMock'))
},
+ mockObject(value: T) {
+ return _mocker().mockObject({ value }).value
+ },
+
// this is typed in the interface so it's not necessary to type it here
mocked(item: T, _options = {}): any {
return item
diff --git a/test/core/test/vi.spec.ts b/test/core/test/vi.spec.ts
index a030fa6e0..2748c89a8 100644
--- a/test/core/test/vi.spec.ts
+++ b/test/core/test/vi.spec.ts
@@ -200,4 +200,38 @@ describe('testing vi utils', () => {
expect(mod).toBeDefined()
expect(mod.timeout).toBe(100)
})
+
+ test('mockObject', () => {
+ const original = {
+ simple: () => 'value',
+ nested: {
+ method: () => 'real',
+ },
+ prop: 'foo',
+ }
+
+ const mocked = vi.mockObject(original)
+ expect(mocked.simple()).toBe(undefined)
+ expect(mocked.nested.method()).toBe(undefined)
+ expect(mocked.prop).toBe('foo')
+ mocked.simple.mockReturnValue('mocked')
+ mocked.nested.method.mockReturnValue('mocked nested')
+ expect(mocked.simple()).toBe('mocked')
+ expect(mocked.nested.method()).toBe('mocked nested')
+
+ class OriginalClass {
+ constructor() {
+ throw new Error('should be mocked!')
+ }
+
+ someFn() {
+ return 'value'
+ }
+ }
+ const MockedClass = vi.mockObject(OriginalClass)
+ const mockedInstance = new MockedClass()
+ expect(MockedClass).toHaveBeenCalled()
+ vi.mocked(mockedInstance).someFn.mockImplementation(() => 'mocked')
+ expect(mockedInstance.someFn()).toBe('mocked')
+ })
})