[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

fix(spy): allow classes in `vi.mocked` utility (#8839)

authored by

Vladimir and committed by
GitHub
(Oct 27, 2025, 1:25 PM +0100) f87568d6 c0f0c34b

+86 -37
+28 -22
packages/spy/src/types.ts
··· 370 370 ? Promise<Partial<Awaited<T>>> 371 371 : Partial<T> 372 372 373 - export interface PartialMock<T extends Procedure = Procedure> 374 - extends MockInstance< 375 - (...args: Parameters<T>) => PartialMaybePromise<ReturnType<T>> 376 - > { 377 - new (...args: Parameters<T>): ReturnType<T> 378 - (...args: Parameters<T>): ReturnType<T> 379 - } 373 + type PartialResultFunction<T> = T extends Constructable 374 + ? ({ 375 + new (...args: ConstructorParameters<T>): InstanceType<T> 376 + }) 377 + | ({ 378 + (this: InstanceType<T>, ...args: ConstructorParameters<T>): void 379 + }) 380 + : T extends Procedure 381 + ? (...args: Parameters<T>) => PartialMaybePromise<ReturnType<T>> 382 + : T 380 383 381 - export type MaybeMockedConstructor<T> = T extends new ( 382 - ...args: Array<any> 383 - ) => infer R 384 - ? Mock<(...args: ConstructorParameters<T>) => R> 384 + export interface PartialMock<T extends Procedure | Constructable = Procedure> 385 + extends Mock< 386 + PartialResultFunction<T extends Mock 387 + ? NonNullable<ReturnType<T['getMockImplementation']>> 388 + : T> 389 + > {} 390 + 391 + export type MaybeMockedConstructor<T> = T extends Constructable 392 + ? Mock<T> 385 393 : T 386 - export type MockedFunction<T extends Procedure> = Mock<T> & { 394 + export type MockedFunction<T extends Procedure | Constructable> = Mock<T> & { 387 395 [K in keyof T]: T[K]; 388 396 } 389 - export type PartiallyMockedFunction<T extends Procedure> = PartialMock<T> & { 397 + export type PartiallyMockedFunction<T extends Procedure | Constructable> = PartialMock<T> & { 390 398 [K in keyof T]: T[K]; 391 399 } 392 - export type MockedFunctionDeep<T extends Procedure> = Mock<T> 400 + export type MockedFunctionDeep<T extends Procedure | Constructable> = Mock<T> 393 401 & MockedObjectDeep<T> 394 - export type PartiallyMockedFunctionDeep<T extends Procedure> = PartialMock<T> 402 + export type PartiallyMockedFunctionDeep<T extends Procedure | Constructable> = PartialMock<T> 395 403 & MockedObjectDeep<T> 396 404 export type MockedObject<T> = MaybeMockedConstructor<T> & { 397 405 [K in Methods<T>]: T[K] extends Procedure ? MockedFunction<T[K]> : T[K]; ··· 400 408 [K in Methods<T>]: T[K] extends Procedure ? MockedFunctionDeep<T[K]> : T[K]; 401 409 } & { [K in Properties<T>]: MaybeMockedDeep<T[K]> } 402 410 403 - export type MaybeMockedDeep<T> = T extends Procedure 411 + export type MaybeMockedDeep<T> = T extends Procedure | Constructable 404 412 ? MockedFunctionDeep<T> 405 413 : T extends object 406 414 ? MockedObjectDeep<T> 407 415 : T 408 416 409 - export type MaybePartiallyMockedDeep<T> = T extends Procedure 417 + export type MaybePartiallyMockedDeep<T> = T extends Procedure | Constructable 410 418 ? PartiallyMockedFunctionDeep<T> 411 419 : T extends object 412 420 ? MockedObjectDeep<T> 413 421 : T 414 422 415 - export type MaybeMocked<T> = T extends Procedure 423 + export type MaybeMocked<T> = T extends Procedure | Constructable 416 424 ? MockedFunction<T> 417 425 : T extends object 418 426 ? MockedObject<T> 419 427 : T 420 428 421 - export type MaybePartiallyMocked<T> = T extends Procedure 429 + export type MaybePartiallyMocked<T> = T extends Procedure | Constructable 422 430 ? PartiallyMockedFunction<T> 423 431 : T extends object 424 432 ? MockedObject<T> ··· 428 436 new (...args: any[]): any 429 437 } 430 438 431 - export type MockedClass<T extends Constructable> = MockInstance< 432 - (...args: ConstructorParameters<T>) => InstanceType<T> 433 - > & { 439 + export type MockedClass<T extends Constructable> = MockInstance<T> & { 434 440 prototype: T extends { prototype: any } ? Mocked<T['prototype']> : never 435 441 } & T 436 442
+58 -15
test/core/test/vi.spec.ts
··· 2 2 * @vitest-environment jsdom 3 3 */ 4 4 5 - import type { Mock, MockedFunction, MockedObject, MockInstance } from 'vitest' 5 + import type { Mock, Mocked, MockedFunction, MockedObject, MockInstance } from 'vitest' 6 6 import { describe, expect, expectTypeOf, test, vi } from 'vitest' 7 7 import { getWorkerState } from '../../../packages/vitest/src/runtime/utils' 8 8 ··· 56 56 baz: string 57 57 } 58 58 59 - const mockFactory = vi.fn<() => FooBar>() 59 + const mockFnFactory = vi.fn<() => FooBar>() 60 60 61 - vi.mocked(mockFactory, { partial: true }).mockReturnValue({ 61 + vi.mocked(mockFnFactory, { partial: true }).mockReturnValue({ 62 62 foo: vi.fn(), 63 63 }) 64 64 65 - vi.mocked(mockFactory, { partial: true, deep: false }).mockReturnValue({ 65 + vi.mocked(mockFnFactory, { partial: true, deep: false }).mockReturnValue({ 66 66 bar: vi.fn<FooBar['bar']>(), 67 67 }) 68 68 69 - vi.mocked(mockFactory, { partial: true, deep: true }).mockReturnValue({ 69 + vi.mocked(mockFnFactory, { partial: true, deep: true }).mockReturnValue({ 70 70 baz: 'baz', 71 71 }) 72 72 73 - const mockFactoryAsync = vi.fn<() => Promise<FooBar>>() 73 + if (0) { 74 + const mockFactory = (): FooBar => ({} as FooBar) 74 75 75 - vi.mocked(mockFactoryAsync, { partial: true }).mockResolvedValue({ 76 - foo: vi.fn(), 77 - }) 76 + vi.mocked(mockFactory, { partial: true }).mockReturnValue({ 77 + foo: vi.fn(), 78 + }) 78 79 79 - vi.mocked(mockFactoryAsync, { partial: true, deep: false }).mockResolvedValue({ 80 - bar: vi.fn<FooBar['bar']>(), 81 - }) 80 + vi.mocked(mockFactory, { partial: true, deep: false }).mockReturnValue({ 81 + bar: vi.fn<FooBar['bar']>(), 82 + }) 82 83 83 - vi.mocked(mockFactoryAsync, { partial: true, deep: true }).mockResolvedValue({ 84 - baz: 'baz', 85 - }) 84 + vi.mocked(mockFactory, { partial: true, deep: true }).mockReturnValue({ 85 + baz: 'baz', 86 + }) 87 + 88 + const mockFactoryAsync = async (): Promise<FooBar> => ({} as FooBar) 89 + 90 + vi.mocked(mockFactoryAsync, { partial: true }).mockResolvedValue({ 91 + foo: vi.fn(), 92 + }) 93 + 94 + vi.mocked(mockFactoryAsync, { partial: true, deep: false }).mockResolvedValue({ 95 + bar: vi.fn<FooBar['bar']>(), 96 + }) 97 + 98 + vi.mocked(mockFactoryAsync, { partial: true, deep: true }).mockResolvedValue({ 99 + baz: 'baz', 100 + }) 101 + } 86 102 87 103 function fetchSomething(): Promise<Response> { 88 104 return fetch('https://vitest.dev/') ··· 91 107 // type check only 92 108 vi.mocked(fetchSomething).mockResolvedValue(new Response(null)) 93 109 vi.mocked(fetchSomething, { partial: true }).mockResolvedValue({ ok: false }) 110 + } 111 + }) 112 + 113 + test('vi.mocked with classes', () => { 114 + class Foo { 115 + constructor(public readonly bar: string) {} 116 + 117 + public getBar(): string { 118 + return this.bar 119 + } 120 + } 121 + class FooMock implements Mocked<Foo> { 122 + readonly barMock: Mock<() => string> = vi.fn() 123 + 124 + public get bar(): string { 125 + return this.barMock() 126 + } 127 + 128 + public getBar: Mock<() => string> = vi 129 + .fn() 130 + .mockImplementation(() => this.barMock()) 131 + } 132 + 133 + // type check only 134 + if (0) { 135 + vi.mocked(Foo).mockImplementation(FooMock) 136 + vi.mocked(Foo).mockImplementation(Foo) 94 137 } 95 138 }) 96 139