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 fromvitest
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
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:
- add the imports for
describe
,it
,expect
, orvi
- 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!)