DEV Community

Cover image for From Jest to Vitest - Migration and Benchmark
Matti Bar-Zeev
Matti Bar-Zeev

Posted on • Edited on

From Jest to Vitest - Migration and Benchmark

In this post join me as I migrate my project’s test runner framework from Jest to Vitest, and check if it is really as they claim - “A blazing fast unit test framework”.

Vitest?

I’m pretty sure you’ve already heard about Vite which is Evan You’s recent promising project for frontend tooling. Benchmarks claim it is super fast in times where bundling and HMR present real challenges for dev velocity.
It was recently brought to my attention that there is another emerging project called Vitest which is supposed to bring that same disruption into the realm of test runners. My curiosity obviously could not let this pass.
Although “Vitest is still in development and not stable yet. It's not recommended to use it in production.” I was eager to find out just how fast we are talking about.

Here's my path for migrating a project using Jest into using Vitest along with some benchmarking.

Benchmarking

My guinea pig is (yet again) my React Word-Search game and its tests, but before I jump in I would like to measure how long it currently takes to run the project’s tests in order to do some comparison at the end.
The Project currently has 4 test files which have 37 tests between them. Not too many, I agree, but I believe it can give a good sense of the difference between Jest and Vitest.

I will measure the tests running time in 2 scenarios:

  • Initial run - where I first launch the test
  • Watch run - where I change a single file and see how long it takes for the tests to run

For each framework I will run these scenarios a few times (with and without cleaning the cache for Jest for the initial run) to make sure I get the average time.

Here are the results for Jest I got:

  • Initial run - 6.5s (without clearing Jest cache its 5.5s)
  • Watch run - 5.5s

Migrating to Vitest

Vitest has a really comprehensive gitHub repo, with some good documentation (considering its age).
I could look into the code examples for React and React Testing Lib and monkey copy-paste it with fingers crossed, but I would like to know what’s really going on, and what is the exact minimum required for one to make this transition.
Before I even start to npm install anything, I’d like to try it as they suggest by running a single test. This following test is passing with Jest, now let’s try it with Vitest:

npx vitest src/components/Confirmation/index.test.js
Enter fullscreen mode Exit fullscreen mode

After confirming the installation of ‘vitest’ we get our feedback from the test runner - who could have guessed, the first error (I must admit that Vitest has a more clearer way of displaying the errors and failed tests):

Image description

Error: Failed to parse source for import analysis because the content contains invalid JS syntax. If you are using JSX, make sure to name the file with the .jsx or .tsx extension.

While Jest has no issue with parsing these files, it appears that Vitest does not know how to parse them, and requires that we change the file names if they contain JSX.
Before I jump into introducing new configurations I would like to see if just changing the file name will help with this error, and it does - changing the name of my test file from index.test.js to index.test.jsx eliminates that error, but now I’m getting a new one:

Image description

describe is not defined

Well, Jest has these globals declared, but it seems that Vitest does not, and we need to import them explicitly. No worries, let’s do that, but before we do, we need to install Vitest. We at least know now that running Vitest just by using npx is not enough when migrating a project to work with it.

npm i vitest -D
Enter fullscreen mode Exit fullscreen mode

Now let’s add the needed imports to our test file:

import { it, describe, expect } from 'vitest';
Enter fullscreen mode Exit fullscreen mode

Oh my, now all my tests fail with a lot of errors flying, but that’s good. Let’s address them one by one:

Image description

document is not defined

This error comes from react-testing-library and it has to do with js-dom support of vitest. I’m going to look for some resources for this… yes, the docs do not fail - it says that adding a docblock or comment specifying the env as js-dom (or dom-happy) will do the trick. I will add it to my test and see how it goes:

/**
* @vitest-environment jsdom
*/

describe('Confirmation component', () => {
   . . .
Enter fullscreen mode Exit fullscreen mode

The tests run again, but still all of them are failing, now with new error:

Image description

Invalid Chai property: toBeInTheDocument

Chai? No, no, no… toBeInTheDocument is not a Chai property.
toBeInTheDocument is an API of the testing-library’s js-dom, and the part responsible to include it and append its assertions is the test setup file (in create react app it is the testSetup.js file on the project root).

In order to let vitest include this file as its own setup we need to create a vitest config, no escape there. Now is a good time to look at the configuration found on the example and check what’s going on in the configuration there. Again, I’m not blindly copy-pasting and so I pick what I know to be relevant to the problem I’m facing.

In order to use the configuration I need to install “vite”. I’m not very keen about it, but if that makes my tests run faster, so be it:

npm i vite -D
Enter fullscreen mode Exit fullscreen mode

I create a configuration file named “vite.config.js” and set the configuration as follows:

import {defineConfig} from 'vite';

export default defineConfig({
   test: {
       globals: true,
       setupFiles: 'src/setupTests.js',
   },
});
Enter fullscreen mode Exit fullscreen mode

As you can see I’m giving the setup file location, which loads the jest-dom needed, and also notice that I have the global property set to “true”. This means that I won’t need to import those global variables Jest comes with like “describe”, “expect” etc. I can remove that import from my test :)
(more information on the configuration can be found here)

Good progress, but do our tests pass now? No, some still don’t. We have another error:

Image description

jest is not defined

Well of course it isn’t. We’re using jest in this test for creating spy/stub functions with jest.fn(), but Vitest has another way of achieving this - it has the same implementation but under “vi”. So instead we need to use vi.fn()

import {vi} from 'vitest';
Enter fullscreen mode Exit fullscreen mode
it('should be able to receive a handler for the "Cancel" button and execute it upon click', () => {
      const onCancellationHandler = vi.fn();
    . . .
});
Enter fullscreen mode Exit fullscreen mode

Hurrah! We have a single test migrated into Vitest :)

I will now attempt to run the entire tests with vitest. I will start by changing my npm script for test to run vitest instead of jest:

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

Let's also add the environment: 'jsdom' to the configuration file so we can avoid adding the env docblock in each test.
Running npm tests, and as you probably guessed it, many tests fail, but the good news is that there is nothing new to the issues we already bumped into before.
It is time to do some benchmarking

Benchmark again and compare

Now it is time to take our statistics again for Vitest:

  • Initial run - 5.30s (nice, but kinda the same as Jest with cache)
  • Watch run 1.25s (wow!)

Let’s put it in a nice table:

Framework Initial run Watch run
Jest 6.50s 5.5s
Vitest 5.30s 1.25s

From this little benchmarking I did here on my own machine, it appears that although the initial runs are slightly in the favor of Vitest, the watch run is a lot faster!
As I see it, there is no question that once Vitest is ready for production you should really consider replacing your current test runner with it. My Word-Search game already has it ;)

As always, if you have any thoughts or comments about what's written here, please share with the rest of us :)

Hey! If you liked what you've just read check out @mattibarzeev on Twitter 🍻

Photo by Florian Steciuk on Unsplash

Top comments (25)

Collapse
 
elthrasher profile image
Matt Morgan

My friends, family and colleagues told me I shouldn't run off after another test framework, but now I know we're right and they're wrong. Thanks for this article!

Collapse
 
jjhiggz profile image
Jonathan Higger • Edited

I've been using vitest because of remix stacks. Almost everything feels the same as jest but I notice a few things pragmatically.

Pros

  • nicer coloring
  • faster tests
  • less finnicky config maybe

Cons

  • it seems to crash a bit more often
  • .only and .skip don't seem to work as well for me
Collapse
 
iamthebuilder profile image
The Builder

jest setup has been stressing me out all day, I'm happy that at I came across vitest today and then this article helped me solve the document is not defined issue that i ran into when using vitest. Thanks for writing

Collapse
 
mbarzeev profile image
Matti Bar-Zeev

Glad I could help 🍻

Collapse
 
imamdev_ profile image
Imamuzzaki Abu Salam

Love this ❤️

Collapse
 
mbarzeev profile image
Matti Bar-Zeev

Thanks!

Collapse
 
dopamine0 profile image
Roee Fl

You showed how to define setupFiles in vite.config, but you did not reference the contents of setupTests.js.

Could you include what should be in there for the Chai issue to resolve?

Collapse
 
nibble2 profile image
Nibble

Thank you for this post!
Can I use vitest for company new project now?

Collapse
 
mbarzeev profile image
Matti Bar-Zeev

I believe you can, but make sure it answers all your company's needs

Collapse
 
cawabunga profile image
Emil

Vitest is not that fast as it pretends to be. Jest is faster in most cases, because usually you expect isolation in tests, but Vitest has problems with performance. Here is a Github issue github.com/vitest-dev/vitest/issue....

Collapse
 
roblevintennis profile image
Rob Levin

I’m definitely keeping my eyes on vitest and this post makes me wanna give it another shot (forget exactly why but had trouble getting it working a few weeks back) but the whole philosophy of vitest is really compelling. Thanks for the nice case study!

Collapse
 
mbarzeev profile image
Matti Bar-Zeev

With pleasure :)

Collapse
 
larsejaas profile image
Lars Ejaas

Oh, that looked surprisingly simple!
I am still a bit unsure how enzyme would fit into the Vitest environment and how easy it is to setup with next.js - but the Vitest project sounds SO promising!

Collapse
 
cawabunga profile image
Emil

It is suggested to drop enzyme tests, because it is not supported any more. Keeping aside that it brings questionable value.

Collapse
 
larsejaas profile image
Lars Ejaas

Thanks Emil. I have also come to that conclusion myself 🙂

Collapse
 
vaishnavi1029 profile image
vaishnavi1029

Remix Vite plugin can't detect preamble. Something is wrong.
❯ app/routes/stored.tsx:2:8
1| export default function Comp(){
2| return <h1>jcs</h1>

Error: Remix Vite plugin can't detect preamble. Something is wrong.
here is my vite.config
///
///
import { vitePlugin as remix } from "@remix-run/dev";
import { installGlobals } from "@remix-run/node";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
installGlobals();

export default defineConfig({
test:{
environment:"jsdom",
globals: true,
setupFiles: ["./set-up-test.env.ts"],
},
plugins: [
remix({
ignoredRouteFiles: ["*/.css"],
}),
tsconfigPaths(),

],

});
can you help me solve this