···78787979This change also removes the file from `expect.getState().currentTestName` and makes `expect.getState().testPath` required.
80808181+### Simplified generic types of mock functions (e.g. `vi.fn<T>`, `Mock<T>`)
8282+8383+Previously `vi.fn<TArgs, TReturn>` accepted two generic types separately for arguemnts and return value. This is changed to directly accept a function type `vi.fn<T>` to simplify the usage.
8484+8585+```ts
8686+import { type Mock, vi } from 'vitest'
8787+8888+const add = (x: number, y: number): number => x + y
8989+9090+// using vi.fn<T>
9191+const mockAdd = vi.fn<Parameters<typeof add>, ReturnType<typeof add>>() // [!code --]
9292+const mockAdd = vi.fn<typeof add>() // [!code ++]
9393+9494+// using Mock<T>
9595+const mockAdd: Mock<Parameters<typeof add>, ReturnType<typeof add>> = vi.fn() // [!code --]
9696+const mockAdd: Mock<typeof add> = vi.fn() // [!code ++]
9797+```
9898+8199## Migrating to Vitest 1.0
8210083101<!-- introduction -->
···324342Vitest doesn't have an equivalent to `jest` namespace, so you will need to import types directly from `vitest`:
325343326344```ts
327327-let fn: jest.Mock<string, [string]> // [!code --]
345345+let fn: jest.Mock<(name: string) => number> // [!code --]
328346import type { Mock } from 'vitest' // [!code ++]
329329-let fn: Mock<[string], string> // [!code ++]
347347+let fn: Mock<(name: string) => number> // [!code ++]
330348```
331331-332332-Also, Vitest has `Args` type as a first argument instead of `Returns`, as you can see in diff.
333349334350### Timers
335351
+92-111
packages/spy/src/index.ts
···3838 | MockSettledResultFulfilled<T>
3939 | MockSettledResultRejected
40404141-export interface MockContext<TArgs, TReturns> {
4141+export interface MockContext<T extends Procedure> {
4242 /**
4343 * This is an array containing all arguments for each call. One item of the array is the arguments of that call.
4444 *
···5353 * ['arg3'], // second call
5454 * ]
5555 */
5656- calls: TArgs[]
5656+ calls: Parameters<T>[]
5757 /**
5858 * This is an array containing all instances that were instantiated when mock was called with a `new` keyword. Note that this is an actual context (`this`) of the function, not a return value.
5959 */
6060- instances: TReturns[]
6060+ instances: ReturnType<T>[]
6161 /**
6262 * The order of mock's execution. This returns an array of numbers which are shared between all defined mocks.
6363 *
···101101 * },
102102 * ]
103103 */
104104- results: MockResult<TReturns>[]
104104+ results: MockResult<ReturnType<T>>[]
105105 /**
106106 * An array containing all values that were `resolved` or `rejected` from the function.
107107 *
···129129 * },
130130 * ]
131131 */
132132- settledResults: MockSettledResult<Awaited<TReturns>>[]
132132+ settledResults: MockSettledResult<Awaited<ReturnType<T>>>[]
133133 /**
134134 * This contains the arguments of the last call. If spy wasn't called, will return `undefined`.
135135 */
136136- lastCall: TArgs | undefined
136136+ lastCall: Parameters<T> | undefined
137137}
138138139139type Procedure = (...args: any[]) => any
···150150}[keyof T] &
151151(string | symbol)
152152153153-/**
154154- * @deprecated Use MockInstance<A, R> instead
155155- */
156156-export interface SpyInstance<TArgs extends any[] = any[], TReturns = any>
157157- extends MockInstance<TArgs, TReturns> {}
153153+/*
154154+cf. https://typescript-eslint.io/rules/method-signature-style/
158155159159-export interface MockInstance<TArgs extends any[] = any[], TReturns = any> {
156156+Typescript assignability is different between
157157+ { foo: (f: T) => U } (this is "method-signature-style")
158158+and
159159+ { foo(f: T): U }
160160+161161+Jest uses the latter for `MockInstance.mockImplementation` etc... and it allows assignment such as:
162162+ const boolFn: Jest.Mock<() => boolean> = jest.fn<() => true>(() => true)
163163+*/
164164+/* eslint-disable ts/method-signature-style */
165165+export interface MockInstance<T extends Procedure = Procedure> {
160166 /**
161167 * Use it to return the name given to mock with method `.mockName(name)`.
162168 */
163163- getMockName: () => string
169169+ getMockName(): string
164170 /**
165171 * Sets internal mock name. Useful to see the name of the mock if an assertion fails.
166172 */
167167- mockName: (n: string) => this
173173+ mockName(n: string): this
168174 /**
169175 * Current context of the mock. It stores information about all invocation calls, instances, and results.
170176 */
171171- mock: MockContext<TArgs, TReturns>
177177+ mock: MockContext<T>
172178 /**
173179 * Clears all information about every call. After calling it, all properties on `.mock` will return an empty state. This method does not reset implementations.
174180 *
175181 * It is useful if you need to clean up mock between different assertions.
176182 */
177177- mockClear: () => this
183183+ mockClear(): this
178184 /**
179185 * Does what `mockClear` does and makes inner implementation an empty function (returning `undefined` when invoked). This also resets all "once" implementations.
180186 *
181187 * This is useful when you want to completely reset a mock to the default state.
182188 */
183183- mockReset: () => this
189189+ mockReset(): this
184190 /**
185191 * Does what `mockReset` does and restores inner implementation to the original function.
186192 *
187193 * Note that restoring mock from `vi.fn()` will set implementation to an empty function that returns `undefined`. Restoring a `vi.fn(impl)` will restore implementation to `impl`.
188194 */
189189- mockRestore: () => void
195195+ mockRestore(): void
190196 /**
191197 * Returns current mock implementation if there is one.
192198 *
···194200 *
195201 * If mock was created with `vi.spyOn`, it will return `undefined` unless a custom implementation was provided.
196202 */
197197- getMockImplementation: () => ((...args: TArgs) => TReturns) | undefined
203203+ getMockImplementation(): T | undefined
198204 /**
199205 * Accepts a function that will be used as an implementation of the mock.
200206 * @example
201207 * const increment = vi.fn().mockImplementation(count => count + 1);
202208 * expect(increment(3)).toBe(4);
203209 */
204204- mockImplementation: (fn: (...args: TArgs) => TReturns) => this
210210+ mockImplementation(fn: T): this
205211 /**
206212 * Accepts a function that will be used as a mock implementation during the next call. Can be chained so that multiple function calls produce different results.
207213 * @example
···209215 * expect(fn(3)).toBe(4);
210216 * expect(fn(3)).toBe(3);
211217 */
212212- mockImplementationOnce: (fn: (...args: TArgs) => TReturns) => this
218218+ mockImplementationOnce(fn: T): this
213219 /**
214220 * Overrides the original mock implementation temporarily while the callback is being executed.
215221 * @example
···221227 *
222228 * myMockFn() // 'original'
223229 */
224224- withImplementation: <T>(
225225- fn: (...args: TArgs) => TReturns,
226226- cb: () => T
227227- ) => T extends Promise<unknown> ? Promise<this> : this
230230+ withImplementation<T2>(fn: T, cb: () => T2): T2 extends Promise<unknown> ? Promise<this> : this
231231+228232 /**
229233 * Use this if you need to return `this` context from the method without invoking actual implementation.
230234 */
231231- mockReturnThis: () => this
235235+ mockReturnThis(): this
232236 /**
233237 * Accepts a value that will be returned whenever the mock function is called.
234238 */
235235- mockReturnValue: (obj: TReturns) => this
239239+ mockReturnValue(obj: ReturnType<T>): this
236240 /**
237241 * Accepts a value that will be returned during the next function call. If chained, every consecutive call will return the specified value.
238242 *
···247251 * // 'first call', 'second call', 'default'
248252 * console.log(myMockFn(), myMockFn(), myMockFn())
249253 */
250250- mockReturnValueOnce: (obj: TReturns) => this
254254+ mockReturnValueOnce(obj: ReturnType<T>): this
251255 /**
252256 * Accepts a value that will be resolved when async function is called.
253257 * @example
254258 * const asyncMock = vi.fn().mockResolvedValue(42)
255259 * asyncMock() // Promise<42>
256260 */
257257- mockResolvedValue: (obj: Awaited<TReturns>) => this
261261+ mockResolvedValue(obj: Awaited<ReturnType<T>>): this
258262 /**
259263 * Accepts a value that will be resolved during the next function call. If chained, every consecutive call will resolve specified value.
260264 * @example
···267271 * // Promise<'first call'>, Promise<'second call'>, Promise<'default'>
268272 * console.log(myMockFn(), myMockFn(), myMockFn())
269273 */
270270- mockResolvedValueOnce: (obj: Awaited<TReturns>) => this
274274+ mockResolvedValueOnce(obj: Awaited<ReturnType<T>>): this
271275 /**
272276 * Accepts an error that will be rejected when async function is called.
273277 * @example
274278 * const asyncMock = vi.fn().mockRejectedValue(new Error('Async error'))
275279 * await asyncMock() // throws 'Async error'
276280 */
277277- mockRejectedValue: (obj: any) => this
281281+ mockRejectedValue(obj: any): this
278282 /**
279283 * Accepts a value that will be rejected during the next function call. If chained, every consecutive call will reject specified value.
280284 * @example
···286290 * await asyncMock() // first call
287291 * await asyncMock() // throws "Async error"
288292 */
289289- mockRejectedValueOnce: (obj: any) => this
293293+ mockRejectedValueOnce(obj: any): this
294294+}
295295+/* eslint-enable ts/method-signature-style */
296296+297297+export interface Mock<T extends Procedure = Procedure>
298298+ extends MockInstance<T> {
299299+ new (...args: Parameters<T>): ReturnType<T>
300300+ (...args: Parameters<T>): ReturnType<T>
290301}
291302292292-export interface Mock<TArgs extends any[] = any, TReturns = any>
293293- extends MockInstance<TArgs, TReturns> {
294294- new (...args: TArgs): TReturns
295295- (...args: TArgs): TReturns
296296-}
297297-export interface PartialMock<TArgs extends any[] = any, TReturns = any>
303303+type PartialMaybePromise<T> = T extends Promise<Awaited<T>>
304304+ ? Promise<Partial<Awaited<T>>>
305305+ : Partial<T>
306306+307307+export interface PartialMock<T extends Procedure = Procedure>
298308 extends MockInstance<
299299- TArgs,
300300- TReturns extends Promise<Awaited<TReturns>>
301301- ? Promise<Partial<Awaited<TReturns>>>
302302- : Partial<TReturns>
309309+ (...args: Parameters<T>) => PartialMaybePromise<ReturnType<T>>
303310 > {
304304- new (...args: TArgs): TReturns
305305- (...args: TArgs): TReturns
311311+ new (...args: Parameters<T>): ReturnType<T>
312312+ (...args: Parameters<T>): ReturnType<T>
306313}
307314308315export type MaybeMockedConstructor<T> = T extends new (
309316 ...args: Array<any>
310317) => infer R
311311- ? Mock<ConstructorParameters<T>, R>
318318+ ? Mock<(...args: ConstructorParameters<T>) => R>
312319 : T
313313-export type MockedFunction<T extends Procedure> = Mock<
314314- Parameters<T>,
315315- ReturnType<T>
316316-> & {
320320+export type MockedFunction<T extends Procedure> = Mock<T> & {
317321 [K in keyof T]: T[K];
318322}
319319-export type PartiallyMockedFunction<T extends Procedure> = PartialMock<
320320- Parameters<T>,
321321- ReturnType<T>
322322-> & {
323323+export type PartiallyMockedFunction<T extends Procedure> = PartialMock<T> & {
323324 [K in keyof T]: T[K];
324325}
325325-export type MockedFunctionDeep<T extends Procedure> = Mock<
326326- Parameters<T>,
327327- ReturnType<T>
328328-> &
329329-MockedObjectDeep<T>
330330-export type PartiallyMockedFunctionDeep<T extends Procedure> = PartialMock<
331331- Parameters<T>,
332332- ReturnType<T>
333333-> &
334334-MockedObjectDeep<T>
326326+export type MockedFunctionDeep<T extends Procedure> = Mock<T> &
327327+ MockedObjectDeep<T>
328328+export type PartiallyMockedFunctionDeep<T extends Procedure> = PartialMock<T> &
329329+ MockedObjectDeep<T>
335330export type MockedObject<T> = MaybeMockedConstructor<T> & {
336331 [K in Methods<T>]: T[K] extends Procedure ? MockedFunction<T[K]> : T[K];
337332} & { [K in Properties<T>]: T[K] }
···368363}
369364370365export type MockedClass<T extends Constructable> = MockInstance<
371371- T extends new (...args: infer P) => any ? P : never,
372372- InstanceType<T>
366366+ (...args: ConstructorParameters<T>) => InstanceType<T>
373367> & {
374368 prototype: T extends { prototype: any } ? Mocked<T['prototype']> : never
375369} & T
376370377371export type Mocked<T> = {
378378- [P in keyof T]: T[P] extends (...args: infer Args) => infer Returns
379379- ? MockInstance<Args, Returns>
372372+ [P in keyof T]: T[P] extends Procedure
373373+ ? MockInstance<T[P]>
380374 : T[P] extends Constructable
381375 ? MockedClass<T[P]>
382376 : T[P];
···394388 obj: T,
395389 methodName: S,
396390 accessType: 'get'
397397-): MockInstance<[], T[S]>
391391+): MockInstance<() => T[S]>
398392export function spyOn<T, G extends Properties<Required<T>>>(
399393 obj: T,
400394 methodName: G,
401395 accessType: 'set'
402402-): MockInstance<[T[G]], void>
396396+): MockInstance<(arg: T[G]) => void>
403397export function spyOn<T, M extends Classes<Required<T>> | Methods<Required<T>>>(
404398 obj: T,
405399 methodName: M
406400): Required<T>[M] extends
407401| { new (...args: infer A): infer R }
408402| ((...args: infer A) => infer R)
409409- ? MockInstance<A, R>
403403+ ? MockInstance<(...args: A) => R>
410404 : never
411405export function spyOn<T, K extends keyof T>(
412406 obj: T,
···426420427421let callOrder = 0
428422429429-function enhanceSpy<TArgs extends any[], TReturns>(
430430- spy: SpyInternalImpl<TArgs, TReturns>,
431431-): MockInstance<TArgs, TReturns> {
432432- const stub = spy as unknown as MockInstance<TArgs, TReturns>
423423+function enhanceSpy<T extends Procedure>(
424424+ spy: SpyInternalImpl<Parameters<T>, ReturnType<T>>,
425425+): MockInstance<T> {
426426+ type TArgs = Parameters<T>
427427+ type TReturns = ReturnType<T>
433428434434- let implementation: ((...args: TArgs) => TReturns) | undefined
429429+ const stub = spy as unknown as MockInstance<T>
430430+431431+ let implementation: T | undefined
435432436433 let instances: any[] = []
437434 let invocations: number[] = []
438435439436 const state = tinyspy.getInternalState(spy)
440437441441- const mockContext: MockContext<TArgs, TReturns> = {
438438+ const mockContext: MockContext<T> = {
442439 get calls() {
443440 return state.calls
444441 },
···499496500497 stub.mockReset = () => {
501498 stub.mockClear()
502502- implementation = () => undefined as unknown as TReturns
499499+ implementation = (() => undefined) as T
503500 onceImplementations = []
504501 return stub
505502 }
···512509 }
513510514511 stub.getMockImplementation = () => implementation
515515- stub.mockImplementation = (fn: (...args: TArgs) => TReturns) => {
512512+ stub.mockImplementation = (fn: T) => {
516513 implementation = fn
517514 state.willCall(mockCall)
518515 return stub
519516 }
520517521521- stub.mockImplementationOnce = (fn: (...args: TArgs) => TReturns) => {
518518+ stub.mockImplementationOnce = (fn: T) => {
522519 onceImplementations.push(fn)
523520 return stub
524521 }
525522526526- function withImplementation(
527527- fn: (...args: TArgs) => TReturns,
528528- cb: () => void
529529- ): MockInstance<TArgs, TReturns>
530530- function withImplementation(
531531- fn: (...args: TArgs) => TReturns,
532532- cb: () => Promise<void>
533533- ): Promise<MockInstance<TArgs, TReturns>>
534534- function withImplementation(
535535- fn: (...args: TArgs) => TReturns,
536536- cb: () => void | Promise<void>,
537537- ): MockInstance<TArgs, TReturns> | Promise<MockInstance<TArgs, TReturns>> {
523523+ function withImplementation(fn: T, cb: () => void): MockInstance<T>
524524+ function withImplementation(fn: T, cb: () => Promise<void>): Promise<MockInstance<T>>
525525+ function withImplementation(fn: T, cb: () => void | Promise<void>): MockInstance<T> | Promise<MockInstance<T>> {
538526 const originalImplementation = implementation
539527540528 implementation = fn
···563551 stub.withImplementation = withImplementation
564552565553 stub.mockReturnThis = () =>
566566- stub.mockImplementation(function (this: TReturns) {
554554+ stub.mockImplementation((function (this: TReturns) {
567555 return this
568568- })
556556+ }) as any)
569557570570- stub.mockReturnValue = (val: TReturns) => stub.mockImplementation(() => val)
571571- stub.mockReturnValueOnce = (val: TReturns) =>
572572- stub.mockImplementationOnce(() => val)
558558+ stub.mockReturnValue = (val: TReturns) => stub.mockImplementation((() => val) as any)
559559+ stub.mockReturnValueOnce = (val: TReturns) => stub.mockImplementationOnce((() => val) as any)
573560574561 stub.mockResolvedValue = (val: Awaited<TReturns>) =>
575575- stub.mockImplementation(() => Promise.resolve(val as TReturns) as any)
562562+ stub.mockImplementation((() => Promise.resolve(val as TReturns)) as any)
576563577564 stub.mockResolvedValueOnce = (val: Awaited<TReturns>) =>
578578- stub.mockImplementationOnce(() => Promise.resolve(val as TReturns) as any)
565565+ stub.mockImplementationOnce((() => Promise.resolve(val as TReturns)) as any)
579566580567 stub.mockRejectedValue = (val: unknown) =>
581581- stub.mockImplementation(() => Promise.reject(val) as any)
568568+ stub.mockImplementation((() => Promise.reject(val)) as any)
582569583570 stub.mockRejectedValueOnce = (val: unknown) =>
584584- stub.mockImplementationOnce(() => Promise.reject(val) as any)
571571+ stub.mockImplementationOnce((() => Promise.reject(val)) as any)
585572586573 Object.defineProperty(stub, 'mock', {
587574 get: () => mockContext,
···594581 return stub as any
595582}
596583597597-export function fn<TArgs extends any[] = any, R = any>(): Mock<TArgs, R>
598598-export function fn<TArgs extends any[] = any[], R = any>(
599599- implementation: (...args: TArgs) => R
600600-): Mock<TArgs, R>
601601-export function fn<TArgs extends any[] = any[], R = any>(
602602- implementation?: (...args: TArgs) => R,
603603-): Mock<TArgs, R> {
604604- const enhancedSpy = enhanceSpy(
605605- tinyspy.internalSpyOn({ spy: implementation || (() => {}) }, 'spy'),
606606- )
584584+export function fn<T extends Procedure = Procedure>(
585585+ implementation?: T,
586586+): Mock<T> {
587587+ const enhancedSpy = enhanceSpy(tinyspy.internalSpyOn({ spy: implementation || (() => {}) }, 'spy'))
607588 if (implementation) {
608589 enhancedSpy.mockImplementation(implementation)
609590 }
610591611611- return enhancedSpy as Mock
592592+ return enhancedSpy as any
612593}
+2-2
test/core/test/mock-internals.test.ts
···11import childProcess, { exec } from 'node:child_process'
22import timers from 'node:timers'
33-import { type SpyInstance, afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
33+import { type MockInstance, afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
44import { execDefault, execHelloWorld, execImportAll } from '../src/exec'
55import { dynamicImport } from '../src/dynamic-import'
66···31313232describe('Math.random', () => {
3333 describe('mock is restored', () => {
3434- let spy: SpyInstance
3434+ let spy: MockInstance
35353636 beforeEach(() => {
3737 spy = vi.spyOn(Math, 'random').mockReturnValue(0.1)
+45-10
test/core/test/vi.spec.ts
···22 * @vitest-environment jsdom
33 */
4455-import type { MockedFunction, MockedObject } from 'vitest'
66-import { describe, expect, test, vi } from 'vitest'
55+import type { Mock, MockedFunction, MockedObject } from 'vitest'
66+import { describe, expect, expectTypeOf, test, vi } from 'vitest'
77import { getWorkerState } from '../../../packages/vitest/src/utils'
8899function expectType<T>(obj: T) {
···4141 })
4242 expectType<MockedFunction<() => boolean>>(vi.fn(() => true))
4343 expectType<MockedFunction<() => boolean>>(vi.fn())
4444+4545+ expectType<MockedFunction<() => boolean>>(vi.fn<() => boolean>(() => true))
4646+ expectType<Mock<() => boolean>>(vi.fn<() => boolean>(() => true))
4747+ expectType<() => boolean>(vi.fn(() => true))
4848+4949+ expectType<(v: number) => boolean>(vi.fn())
4450 })
45514652 test('vi partial mocked', () => {
···5056 baz: string
5157 }
52585353- type FooBarFactory = () => FooBar
5454-5555- const mockFactory: FooBarFactory = vi.fn()
5959+ const mockFactory = vi.fn<() => FooBar>()
56605761 vi.mocked(mockFactory, { partial: true }).mockReturnValue({
5862 foo: vi.fn(),
5963 })
60646165 vi.mocked(mockFactory, { partial: true, deep: false }).mockReturnValue({
6262- bar: vi.fn(),
6666+ bar: vi.fn<FooBar['bar']>(),
6367 })
64686569 vi.mocked(mockFactory, { partial: true, deep: true }).mockReturnValue({
6670 baz: 'baz',
6771 })
68726969- type FooBarAsyncFactory = () => Promise<FooBar>
7070-7171- const mockFactoryAsync: FooBarAsyncFactory = vi.fn()
7373+ const mockFactoryAsync = vi.fn<() => Promise<FooBar>>()
72747375 vi.mocked(mockFactoryAsync, { partial: true }).mockResolvedValue({
7476 foo: vi.fn(),
7577 })
76787779 vi.mocked(mockFactoryAsync, { partial: true, deep: false }).mockResolvedValue({
7878- bar: vi.fn(),
8080+ bar: vi.fn<FooBar['bar']>(),
7981 })
80828183 vi.mocked(mockFactoryAsync, { partial: true, deep: true }).mockResolvedValue({
8284 baz: 'baz',
8385 })
8686+ })
8787+8888+ test('vi.fn and Mock type', () => {
8989+ // use case from https://github.com/vitest-dev/vitest/issues/4723#issuecomment-1851034249
9090+9191+ // hypotetical library to be tested
9292+ type SomeFn = (v: string) => number
9393+ function acceptSomeFn(f: SomeFn) {
9494+ f('hi')
9595+ }
9696+9797+ // SETUP
9898+ // no args are allowed even though it's not type safe
9999+ const someFn1: Mock<SomeFn> = vi.fn()
100100+101101+ // argument types are infered
102102+ const someFn2: Mock<SomeFn> = vi.fn((v) => {
103103+ expectTypeOf(v).toEqualTypeOf<string>()
104104+ return 0
105105+ })
106106+107107+ // arguments are not necessary
108108+ const someFn3: Mock<SomeFn> = vi.fn(() => 0)
109109+110110+ // @ts-expect-error wrong return type will be caught
111111+ const someFn4: Mock<SomeFn> = vi.fn(() => '0')
112112+113113+ // TEST
114114+ acceptSomeFn(someFn1)
115115+ expect(someFn1).toBeCalledWith('hi')
116116+ expect(someFn2).not.toBeCalled()
117117+ expect(someFn3).not.toBeCalled()
118118+ expect(someFn4).not.toBeCalled()
84119 })
8512086121 test('can change config', () => {