Skip to content

How to migrate from Jest to Vitest without headaches!

Posted on:December 17, 2023

Introduction

Vitest is a testing library that is API-compatible with Jest. The main advantages over Jest are:

After a few months with both Vitest and Jest in our team, we have decided to give a push and migrate the remaining projects to Vitest only. This will help us to be more efficient on all the projects (it’s not always easy to switch between Jest testing and Vitest testing depending on the project!).

The issue

For a majority of the projects, we have a small number of test files (between 1 and 10). So it’s a quick win to migrate from one testing framework to another. In each test file a couple of modification is needed:

But for one project, this method seems too complex. The project is a NextJs application:

It’s pretty obvious that a single Pull Request will be not a good idea, and it may take too much time to work on this task. In the team, we are not using the Trunk-Based development methodology, but we try to keep our modifications small:

For us, it’s a good way to keep the development flow and not struggle too much with merging issues. So switching 250 tests in a day doesn’t seems to be a realistic objective, even for an experimented developer! Can we find a alternative path?

The baby step idea

As usual when an issue seems too complex, a good approach is to split the problem. Here the best solution we found is to split our migration in multiple smaller steps. But splitting test environment doesn’t seems to be easy, right? Okay let’s describe the methodology!

First step: init the Vitest setup

The first step is to install Vitest without removing Jest. So in the project let’s start by running:

yarn add vitest --dev

Create a new vite.config.ts file with the following:

import { fileURLToPath, URL } from 'url';
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    setupFiles: './vitest-setup.ts',
    environment: 'jsdom',
    // TODO: remove include prop after complete Vitest migration
    include: ['tests-vitest/**/*.{test,spec}.?(c|m)[jt]s?(x)'],
    coverage: {
      reporter: ['lcov', 'text'],
    },
    outputFile: 'coverage/sonar-report.xml',
  },
  resolve: {
    alias: [{ find: '@/', replacement: fileURLToPath(new URL('./', import.meta.url))
      },
});

And a new vitest-setup.ts with:

import "@testing-library/jest-dom/vitest";

(to be able to use jest-dom !)

The idea is too keep all old Jest test in the test/ folder, and move slowly the tests in a new tests-vitest/ folder. We need to set the include prop in the configuration to run only the tests in the specific folder. We are also configuring some coverage with Sonar.

Here is the architecture of the project:

web/
	components/.       # React components
	pages/             # Still using the pages router of NextJs!
	tests/             # Jest tests
	test-vitest/       # Vitest tests

in package.json, we are adding a new command to run Vitest tests:

"scripts": {
	...
	"test": "jest",
    "test:vitest": "vitest",
    ...
}

So we can run Jest tests with yarn test and Vitest tests with yarn test:vitest!

A final modification, we need to tweak the Jest configuration in jest.config.js to search tests only in the tests/ folder:

module.exports = {
	...
	testMatch: ['<rootDir>/tests/**/?(*.)+(spec|test).[jt]s?(x)'],
	...
}

And we can drop a few first tests in tests-vitest to be sure it’s working! The first PR goal was about 4 tests files migrated, so it was a pretty small PR that we were able to validate in a day.

Finally don’t forget to adjust your CI to run both test suite in order to still have a complete coverage.

Second step: increase the Vitest coverage

Then slowly move a couple of test files from tests/ to tests-vitest/ and adjust tests to make then pass. Again the same strategy:

It took us around 11 Pull Requests to move all 82 test files and 257 tests. Only one test have been declared too complicated and we decided (for now) to disable it, we will try to re-enable it later on.

Third step: remove Jest!

Finally it’s time to remove all Jest dependencies! This is the most fun part of the project because you remove a lot dependencies! 🤓 Do not forget to remove also all configuration files (jest.config.js, jest.setup.js…) and we decided to move back all Vitest tests in tests/ in this specific PR (adjust include in vitest.config.ts!).

Conclusion

The task seems at first sight impossible when you want to apply the same rules. But you are free to decide and create new way to work a specific problem! Here instead of dealing with a monster PR with maybe 100 or more modified files, it was much more easy to split the issue and have a temporary setup during the migration time. It took us around 2 weeks to complete the migration (around one PR per day during the migration process!)