Home Writing Reading

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);
});

  • Loading next post...
  • Loading previous post...