Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(vitest): add return type and promisable mockFactory #6139

Merged
8 changes: 5 additions & 3 deletions docs/api/vi.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ This section describes the API that you can use when [mocking a module](/guide/m
### vi.mock

- **Type**: `(path: string, factory?: (importOriginal: () => unknown) => unknown) => void`
- **Type**: `<T>(path: Promise<T>, factory?: (importOriginal: () => T) => unknown) => void`
- **Type**: `<T>(path: Promise<T>, factory?: (importOriginal: () => T) => T | Promise<T>) => void`

Substitutes all imported modules from provided `path` with another module. You can use configured Vite aliases inside a path. The call to `vi.mock` is hoisted, so it doesn't matter where you call it. It will always be executed before all imports. If you need to reference some variables outside of its scope, you can define them inside [`vi.hoisted`](#vi-hoisted) and reference them inside `vi.mock`.

Expand All @@ -31,7 +31,9 @@ Vitest will not mock modules that were imported inside a [setup file](/config/#s

If `factory` is defined, all imports will return its result. Vitest calls factory only once and caches results for all subsequent imports until [`vi.unmock`](#vi-unmock) or [`vi.doUnmock`](#vi-dounmock) is called.

Unlike in `jest`, the factory can be asynchronous. You can use [`vi.importActual`](#vi-importactual) or a helper with the factory passed in as the first argument, and get the original module inside. Vitest also supports a module promise instead of a string in `vi.mock` method for better IDE support (when file is moved, path will be updated, `importOriginal` also inherits the type automatically).
Unlike in `jest`, the factory can be asynchronous. You can use [`vi.importActual`](#vi-importactual) or a helper with the factory passed in as the first argument, and get the original module inside.

Vitest also supports a module promise instead of a string in the `vi.mock` and `vi.doMock` methods for better IDE support. When the file is moved, the path will be updated, and `importOriginal` also inherits the type automatically. Using this signature will also enforce factory return type to be compatible with the original module (but every export is optional).

```ts twoslash
// @filename: ./path/to/module.js
Expand Down Expand Up @@ -139,7 +141,7 @@ If there is no `__mocks__` folder or a factory provided, Vitest will import the
### vi.doMock

- **Type**: `(path: string, factory?: (importOriginal: () => unknown) => unknown) => void`
- **Type**: `<T>(path: Promise<T>, factory?: (importOriginal: () => T) => unknown) => void`
- **Type**: `<T>(path: Promise<T>, factory?: (importOriginal: () => T) => T | Promise<T>) => void`

The same as [`vi.mock`](#vi-mock), but it's not hoisted to the top of the file, so you can reference variables in the global file scope. The next [dynamic import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) of the module will be mocked.

Expand Down
6 changes: 4 additions & 2 deletions packages/vitest/src/types/mocker.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
type Promisable<T> = T | Promise<T>

export type MockFactoryWithHelper<M = unknown> = (
importOriginal: <T extends M>() => Promise<T>
) => any
importOriginal: <T extends M = M>() => Promise<T>
) => Promisable<Partial<M>>
export type MockFactory = () => any

export type MockMap = Map<string, Record<string, string | null | MockFactory>>
Expand Down
1 change: 1 addition & 0 deletions test/config/test/workers-option.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getWorkersCountByPercentage } from 'vitest/src/utils/workers.js'
import * as testUtils from '../../test-utils'

vi.mock(import('node:os'), async importOriginal => ({
...(await importOriginal()),
default: {
...(await importOriginal()).default,
availableParallelism: () => 10,
Expand Down
2 changes: 1 addition & 1 deletion test/core/test/mocking/factory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ vi.mock('../../src/mocks/log.ts', async () => {
},
}
})

// @ts-expect-error null is not allowed to mocked implementation
vi.mock('../../src/mocks/default.ts', () => null)

describe('mocking with factory', () => {
Expand Down