DEV Community

Cover image for How to migrate from Jest to Vitest without headaches!
Julien Bras
Julien Bras

Posted on • Originally published at sidoine.org

How to migrate from Jest to Vitest without headaches!

Introduction

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

  • Vitest is compatible out-of-the-box with TypeScript! No need to deal with ts-jest or other complex setup.
  • Vitest is fast! We have seen a improvement of around 50% on your main module!

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:

  • import the describe, it,expect keywords from vitest as it's not global by default
  • adjust the test if not passing

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

  • 82 test files
  • 257 tests in total

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:

  • not too much code modified each time
  • not too much time between starting creating a feature branch and the merge of the feature (about a day)

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
Enter fullscreen mode Exit fullscreen mode

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))
      },
});
Enter fullscreen mode Exit fullscreen mode

And a new vitest-setup.ts with:

import "@testing-library/jest-dom/vitest";
Enter fullscreen mode Exit fullscreen mode

(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
Enter fullscreen mode Exit fullscreen mode

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

"scripts": {
    ...
    "test": "jest",
    "test:vitest": "vitest",
    ...
}
Enter fullscreen mode Exit fullscreen mode

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)'],
    ...
}
Enter fullscreen mode Exit fullscreen mode

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:

  • add the imports for describe, it, expect, or vi
  • play a bit with the test
  • and voilà!

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!)

Top comments (0)