Skip to content

Commit

Permalink
fix(vitest): throw an error if mock was already loaded when vi.mock i…
Browse files Browse the repository at this point in the history
…s called (#4862)
  • Loading branch information
sheremet-va committed Jan 4, 2024
1 parent 8780fe3 commit e12a5a3
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 7 deletions.
16 changes: 16 additions & 0 deletions docs/guide/common-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,19 @@ export default defineConfig({
}
})
```

## Cannot mock "./mocked-file.js" because it is already loaded

This error happens when `vi.mock` method is called on a module that was already loaded. Vitest throws this error because this call has no effect since cached modules are preferred.

Remember that `vi.mock` is always hoisted - it means that the module was loaded before the test file started executing - most likely in a setup file. To fix the error, remove the import or clear the cache at the end of a setup file - beware that setup file and your test file will reference different modules in that case.

```ts
// setupFile.js
import { vi } from 'vitest'
import { sideEffect } from './mocked-file.js'

sideEffect()

vi.resetModules()
```
2 changes: 2 additions & 0 deletions packages/vitest/src/integrations/vi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ function createVitest(): VitestUtils {
path,
importer,
factory ? () => factory(() => _mocker.importActual(path, importer, _mocker.getMockContext().callstack)) : undefined,
true,
)
},

Expand All @@ -496,6 +497,7 @@ function createVitest(): VitestUtils {
path,
importer,
factory ? () => factory(() => _mocker.importActual(path, importer, _mocker.getMockContext().callstack)) : undefined,
false,
)
},

Expand Down
17 changes: 10 additions & 7 deletions packages/vitest/src/runtime/mocker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export class VitestMocker {
if (mock.type === 'unmock')
this.unmockPath(fsPath)
if (mock.type === 'mock')
this.mockPath(mock.id, fsPath, external, mock.factory)
this.mockPath(mock.id, fsPath, external, mock.factory, mock.throwIfCached)
}))

VitestMocker.pendingIds = []
Expand Down Expand Up @@ -407,10 +407,13 @@ export class VitestMocker {
this.deleteCachedItem(id)
}

public mockPath(originalId: string, path: string, external: string | null, factory?: MockFactory) {
const suitefile = this.getSuiteFilepath()
public mockPath(originalId: string, path: string, external: string | null, factory: MockFactory | undefined, throwIfExists: boolean) {
const id = this.normalizePath(path)

if (throwIfExists && this.moduleCache.has(id))
throw new Error(`[vitest] Cannot mock "${originalId}" because it is already loaded. Did you import it in a setup file?\n\nPlease, remove the import if you want static imports to be mocked, or clear module cache by calling "vi.resetModules()" before mocking if you are going to import the file again. See: https://vitest.dev/guide/common-errors.html#cannot-mock-mocked-file.js-because-it-is-already-loaded`)

const suitefile = this.getSuiteFilepath()
const mocks = this.mockMap.get(suitefile) || {}
const resolves = this.resolveCache.get(suitefile) || {}

Expand Down Expand Up @@ -484,11 +487,11 @@ export class VitestMocker {
return mock
}

public queueMock(id: string, importer: string, factory?: MockFactory) {
VitestMocker.pendingIds.push({ type: 'mock', id, importer, factory })
public queueMock(id: string, importer: string, factory?: MockFactory, throwIfCached = false) {
VitestMocker.pendingIds.push({ type: 'mock', id, importer, factory, throwIfCached })
}

public queueUnmock(id: string, importer: string) {
VitestMocker.pendingIds.push({ type: 'unmock', id, importer })
public queueUnmock(id: string, importer: string, throwIfCached = false) {
VitestMocker.pendingIds.push({ type: 'unmock', id, importer, throwIfCached })
}
}
1 change: 1 addition & 0 deletions packages/vitest/src/types/mocker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export interface PendingSuiteMock {
id: string
importer: string
type: 'mock' | 'unmock'
throwIfCached: boolean
factory?: MockFactory
}

0 comments on commit e12a5a3

Please sign in to comment.