As of 2024, Vitest is all the rage for testing. But can we apply what we learned about mocking modules in Jest to Vitest? Absolutely!
This post expands on Mocking Modules in Jest Tests. The Vite testing environment shares many of the same API interfaces as Jest, so start with Mocking Modules in Jest Tests for detailed information about mocking functions, interfaces, and React components.
The good news is that almost every API method in Jest is available in vitest. The one small, non-trivial, difference involves importing modules for mocking.
Module Imports
Let’s get right to the point—in any place where you are using the actual module, you will need to use importActual
and mark the mock factory function as async
.
vi.mock('./navigation', async () => {
const actualNavigationModule = await vi.importActual('./navigation');
});
Now that I’ve spoiled the ending, allow me to take a step back and explain why you need to use importActual
in the first place.
To review quickly, in Jest, if you want to import a module and mock a single function, the code looks like the following: The actual navigation
module is imported inside the mock factory function, all the actual methods are spread on the factory’s result, and a mocked calculateRoute
method is provided with a different implementation to be used in tests.
// Jest - spaceship.spec.js
let mockErrorId;
vi.mock('./navigation', () => {
const actualNavigationModule = jest.requireActual("./navigation");
return {
...actualNavigationModule,
calculateRoute: (from, to, starDateTimeSeconds, setLastError) => {
const err = actualNavigationModule.errorIdToErrorData(mockErrorId);
setLastError(err);
},
};
});
In Vitest, importing a module uses an async pattern. When you use Vitest’s importActual
method, it returns a Promise that resolves to the module. The asynchronicity of the importActual
method seems like a challenge, but it’s simply solved by making the mock factory function async
.
// vitest - spaceship.spec.js
let mockErrorId;
vi.mock('./navigation', async () => { // <- now async
// Use the `importActual` method and await it.
const actualNavigationModule = await vi.importActual('./navigation');
return {
...actualNavigationModule,
calculateRoute: (from, to, starDateTimeSeconds, setLastError) => {
const err = actualNavigationModule.errorIdToErrorData(mockErrorId);
setLastError(err);
},
};
});
Hooray! Now your Vitest tests complete successfully using the mock code!
Get TypeScript working
Now you’ll want to switch to using TypeScript for type information and checking while writing tests. The default resolved result type of importActual
is ESModuleExports
. This is problematic if you want to access a specific method from the imported module, as you’ll encounter an error like:
'actualNavigationModule.errorIdToErrorData' is of type 'unknown'. ts(18046)
To solve the error you need to type the result of importActual
.
// vitest - spaceship.spec.ts
import type * as NavigationModule from './navigation';
vi.mock('./navigation', async () => {
const actualNavigationModule = await vi.importActual<typeof NavigationModule>(
'./navigation'
);
});
Note that in the above code, you’ve imported the NavigationModule
using the type
keyword, then applied it using typeof
as the result type of importActual
. Now you have access to type information for the navigation module.
Now you can mock modules in both Jest and Vitest! 🎉
Once again, if you’re looking for complete and detailed information on different strategies and methods to mock modules in Jest and Vitest, please see the Mocking Modules in Jest Tests post.
Feeling mocked by mocks?
Drop into our Community Discord for help. Our frontend development consultants are always available to lend a hand. Whether you’re working on testing for a small project or building an enterprise application, we can help you find your way.