til / Vitest hoisted and destructuring
Vitest v0.31.0 introduced vi.hoisted
, which enables us to execute code before imports. This allows us to do things like adjusting the system time for imported code.
vi.hoisted(() => vi.setSystemTime(new Date(1985, 11, 12)))
We couldn’t reference variables inside vi.mock
before, but now that the hoisted code is executed first, we can use it to pass variables to vi.mock
.
const { mockedMethod } = vi.hoisted(() => {
return { mockedMethod: vi.fn() }
})
vi.mock('./path/to/module.js', () => {
return { originalMethod: mockedMethod }
})
To make this all possible, Vitest needs to modify all imports to dynamic imports when we use vi.mock
. This happens behind the scenes.
// Input code
import { it, expect, vi } from 'vitest';
import { value } from './my-module';
vi.mock('./some-file');
test('should be 1', () => {
expect(value).toBe(1);
});
// What Vitest converts it to
const { it, expect, vi } = await import('vitest');
vi.mock('./some-file');
const { value } = await import('./my-module');
test('should be 1', () => {
expect(value).toBe(1); // This won't work
});
We are using destructuring to get the value
from my-module
, but this breaks the getter and we won’t get the right value. There is an open issue about this. As a workaround, we can write the dynamic import ourselves.
import { it, expect, vi } from 'vitest';
const myModule = await import('./my-module');
vi.mock('./some-file');
test('should be 1', () => {
expect(myModule.value).toBe(1);
});