Migrating from Jest to Vitest

Migrating from Jest to Vitest

ยท

4 min read

I recently migrated from create-react-app (CRA) to ViteJS, and as part of that, I switched my test runner from Jest to Vitest.

In this article, I go through all the steps I took as part of the migration, in the hope that it might help others who are going through the same process.

Why switch?

I had originally planned to keep using Jest during the migration of CRA to ViteJS, but I kept running into issues, mainly because Jest support for ES Modules is still experimental. There is a Vite plugin called vite-jest but it's still very much a work in progress.

Vitest is also pretty early in its development stages, but I felt that it was stable enough to give it a try, and I'm sure glad I did. It has many advantages but one thing I really like about it compared to other test runners is that it shares the same config file and plugins as Vite itself, so there's only one single pipeline to worry about.

Migration steps

Here are all of the steps I took to migrate from Jest to Vitest.

Note: These steps are unique to the way Jest was installed within CRA, so they may vary if you installed and configured Jest manually.

Let's get started!

1. Install dependencies

We need to install 3 dependencies:

  • Vitest: Our test runner.
  • jsdom: To mimic the browser while running tests.
  • c8: To generate our code coverage reports.

To install these dev dependencies, run the following command:

npm install --save-dev vitest jsdom c8

2. Update vite.config.ts

As I mentioned previously, the beauty of using Vitest is that it integrates seamlessly with Vite, so all we have to do is update the vite.config.ts file.

You'll also need to add a reference to the Vitest types using a triple slash command at the top of your config file.

/// <reference types="vitest" />
import { defineConfig } from 'vite'

export default defineConfig({
  test: {
    // ...
  },
})

You may have different requirements, but here's what mine ended up looking like:

/// <reference types="vitest" />
import { defineConfig } from 'vite'

export default defineConfig({
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './src/setupTests.ts',
    coverage: {
      reporter: ['text', 'html'],
      exclude: [
        'node_modules/',
        'src/setupTests.ts',
      ],
    },
  },
});

Here's the full list of config options, but I'll briefly explain each one I used:

  • globals: Setting this to true allows you to reference the APIs globally (describe, expect, it, should etc.), just like Jest.
  • environment: This is where you choose what browser-like environment you want to use.
  • setupFiles: This is the path to the setup files which run before each test file. In CRA, this file (setupFiles.ts) is included by default, so I left it as is.
  • coverage: This is the configuration I use for the c8 reporter. I also specify the folders that I exclude from the report.

3. Convert tests

Vitest has been designed with a Jest compatible API (describe, expect, it, should etc.), so migrating tests was an absolute breeze. Unless you mock modules or methods, you probably won't need to do anything here.

I was making use of Jest method mocking, but all I had to was change jest.fn() to vi.fn(). You'll have to import vi in your test if you want to do the same: import { vi } from 'vitest';

4. Update package.json

Let's update the package.json scripts to reference vitest instead of react-scripts.

 "scripts": {
    ...
    "test": "vitest watch",
    "test:no-watch": "vitest run",
    "test:coverage": "vitest run --coverage"
  },

You'll notice I also added a new entry to run code coverage and provided a way to run tests without a watcher, this comes in handy when running tests in a CI/CD pipeline.

We can npm uninstall @types/jest since we won't need it anymore. I have kept @testing-library/jest-dom around though as it's required by React Testing Library.

Finally, I removed any "jest" configuration I had in this file:

  "jest": {
    "collectCoverageFrom": [
      "src/**/*.{js,ts,tsx}",
      "!/node_modules/",
    ]
  },

5. Update tsconfig.json

To get TypeScript working with the global APIs, add vitest/globals to the types field in your tsconfig.json file.

"types": ["vitest/globals", .....]

You may also run into an error like this:

../node_modules/@types/jest/index.d.ts:34:1 - error TS6200: Definitions of the following identifiers conflict with those in another file: test, describe, it, expect, beforeAll, afterAll, beforeEach,

At the time of writing this article, it still seems to be an open issue. However, a workaround I found is to add "skipLibCheck": true, to your tsconfig.json file.

6. Run tests

Hopefully the migration worked for you and all that's left to do now is to run npm test. ๐ŸŽ‰

Final thoughts

Overall I'm extremely happy with Vitest, they've really made the migration so easy. I'm even more impressed with their docs given it's still pretty new, especially the number of examples they have. If you found this article helpful or have suggestions, please leave a comment below. ๐Ÿ™‚

Want to see more?

I mainly write about real tech topics I face in my everyday life as a Frontend Developer. If this appeals to you then feel free to follow me on Twitter: https://twitter.com/cmacdonnacha

Bye for now ๐Ÿ‘‹

ย