Jest ▶ Vitest Migration

ankita srivastava
6 min readNov 22, 2022
https://www.google.com/url?sa=i&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D0sjAhzv1aSA&psig=AOvVaw3wZ_HO1Hu8iYotq0HAjzH1&ust=1668875804547000&source=images&cd=vfe&ved=0CBEQjhxqFwoTCICKuv-UuPsCFQAAAAAdAAAAABAD

I started with vite migration and found vite performance a lot better compared to vue-cli build and dev server start up time. So, as I already told my vite migration story in my previous blog here, in case anyone is interested in reading that as well, After my vitest migration, when I ran my unit tests, all tests started breaking, which led me to start my vitest migration. So let’s begin with vitest migration.

Migration Guide

Step 1: First, you need to install the below dependencies to get started with vitest

npm install --save-dev vitest jsdom c8

We are installing the below packages:

  • Vitest: As our test runner
  • jsdom: As we don’t have an actual dom in the test, so to have something like actual dom in our tests
  • c8/istanbull: Both comes for coverage report for our unit tests. c8 being the default one i.e.., if you just install c8 and istanbul but don’t tell vitest which one to use for coverage, it will start generating your coverage report using c8.

Step 2: Now you must update your vite.config.ts file with test configuration, as shown below:

test: {
environment: 'jsdom',
coverage: {
reporter: ['text', 'html'],
exclude: [
'node_modules/',
'./tests/vitest.setup.ts',
],
all: true,
lines: 80,
functions: 80,
branches: 80,
statements: 80,
},
setupFiles: ['./tests/vitest.setup.ts'],
globals: true,
}

A few details must be addressed here:

  • Similar to the Jest setup file, we will be creating a file for the Vitest setup, and you need to provide the path for that file in test.setupFiles.
  • I have added an environment called “jsdom”; as we do not have an actual DOM, we are using jsdom to mimic browser behavior.
  • I have set up my coverage configuration to exclude my node_modules and src/vitest.setup.ts files so that vitest will generate a coverage report excluding them. Also, I have set up a threshold, mentioning that metrics should be at least 80%, otherwise the coverage will fail. All of this is very similar to Jest.
  • In Jest, by default, expect, describe, etc. used to be present, and that’s why we don’t need to import them explicitly. Vitest does not have these packages available by default, and hence either we need to import them everywhere or we can configure Vitest to make them globally available by setting globals: true.
  • One more important point to note, vitest.setup.ts is the file that will have the same configuration that you used to keep with Jest, which can be different in everyone else's case. Make sure you import this in your vitest setup file: import ‘@testing-library/jest-dom/extend-expect’; otherwise you will get an error - Error: Invalid Chai property: toHaveTextContent etc, as this import is responsible for including all such things in our tests. This step is required if you are using testing-library.

Step 3: We will now use vitest to replace Jest functions, spy, and stubs. You need to import { vi } from vitest, and then all jest.fn() will become vi.fn().

Step 4: After removing Jest from everywhere, we will update our package.json file scripts containing the following:

"scripts": {
"serve": "vite",
"serve:prod": "vite preview",
"build": "vite build",
"test": "TZ=UTC vitest run",
"test:coverage": "TZ=UTC vitest run --coverage",
"test:watch": "TZ=UTC vitest",
},
  • serve: This will spin up your application locally.
  • serve:prod: This will serve the application from the local ./dist folder
  • build: This will create a production build locally.
  • test: For my application, I need timezone to be in UTC so I have to set TZ=UTC, If you don’t need that you can simply omit it and it will run all you test with coverage.
  • test:coverage: This will run all the tests with coverage.
  • test:watch: It will also run all the test but in watch mode.

Now, you can uninstall @types/jest, @vue/cli-plugin-unit-jest and vue-jest from your package.json devDependencies.

Step 5: If you are using typescript in your project, then you need to add vite/client and vitest/global in your tsconfig file like below:

"types": [
"vite/client",
"vitest/globals",
],

And Tada! 🎊 You run npm run test and you should be able to see all your tests running perfectly fine 👏.

But wait, if you’re still having problems and a lot of tests are failing for you, as they did for me, please consider the following::

  1. This might be very specific to my project, but it might help someone else, so I’m sharing it here. As mentioned in vite migration blog, I had a dynamic component for Icon.vue that was rendering as an HTML tag in tests before migration, but after migration it started coming as raw svg code, so to fix it, I replaced the html assertions with snapshot tests.
  2. Then I have a few tests that were spying on a window object, as shown below:
// Before migration
windowSpy.mockImplementation(() => ({ history: { length: 3 }}));

After vitest migration, this was not working because of vitest architectural choice, it uses different instances of the window object in test and source code. As a result, we can’t see a window object in the vitest.

Now for fixing this tests, we need history.length to be available so I used stubGlobal provided by vitest.

vi.stubGlobal('history', { length: 2, go: vi.fn() })

vitest stubGlobal will put the variable in global scope, now whenever history.go() or history.length will be called, this stub will be used.

3. ) I was having lot of tests dependent on timezone as well. In Jest, we are able to set up timezone before tests starts up, in process.env. But I searched a lot in vitest to set the timezone in environment variable, the only way I could figure out is by running the command TZ=UTC vitest run which will set my timezone to UTC before running the tests.

Now this works fine for terminal, but what if I want to run a single file from IDE which does not use this command from package.json file. I searched for few plugins to check if I can get vitest support in IDE( I was using IntelliJ) so that I can set environment variable for all my tests. I found that IntelliJ latest version(2022.3, still in beta) have vitest support, but with this also you can set up environment variable just for a single file 😕. That means whenever you are running any new file, you need to go and set the environment variable using Edit configuration setting.

Note: As IntelliJ latest version was in beta, I used Jetbrains toolbox to keep it separate with my existing IDE setup. So I am not suggesting you to remove your existing version or IDE, still you can have latest version of IDE, which definitely have more verbose errors when vitest test fails and better support for vitest.

4.) Another point I emphasized in my vite migration blog as well was checking for conflicting versions in package.json. For my project, I was using the vee-validate library for validations, and because there was an issue with the previous versions of this library and it was not compatible with vite, it started giving wrong data, which failed a lot of tests for me. I am adding my package.json version below so that in case you end up with this issue, you know the library versions that at least worked for me:

"dependencies": {
"@apollo/client": "^3.7.1",
"@vue/apollo-composable": "4.0.0-alpha.19",
"@vitejs/plugin-vue": "3.2.0",
"eslint-import-resolver-typescript": "^3.5.2",
"graphql": "^16.5.0",
"vee-validate": "4.5.11",
"vite": "^3.0.0",
"vue": "3.2.33",
"vue-router": "4.0.14",
},

Also, try to avoid using ^ or ~ where there are strict versions given in the above JSON file, this literally ate a lot of my time fixing it.

If you are taking care of these points and have followed all the steps in this blog, you should be able to run all your tests perfectly fine. But, if you are still facing any problems or need any help with migration, please feel free to leave a comment. That’s all I want to share, and I hope it will be helpful to someone who is trying to migrate to vitest from jest. Also, if you have any feedback for the blog, feel free to leave a comment 😊

--

--