How to Get Up and Running With Vitest
Vitest, the unit test framework powered by Vite shares many similarities with Jest. On the other hand, it offers much more and improves on it. Vitest is built on top of the Jest API, so if you already know how to work with Jest, you will know how to work with Vitest too.
This not only makes the learning curve very shallow but also helps ease the migration process in case you would like to switch from Jest to Vitest. But why Vitest in the first place? Let's take a look at some of the features of Vitest to see what it has to offer, then we will see how you can get up and running with Vitest.
Features of Vitest
First and foremost, Vitest is compatible with Jest, which means most of your existing unit tests will work with Vitest too. It uses the same API meaning you don't have to learn new ways of writing unit tests.
Apart from the similarity it shares with the Jest API, it comes with a handful of improvements on top of Jest which could make it a better choice:
- Performance: Vitest is built on top of Vite. It uses multi-threading workers, allowing tests to run simultaneously. This makes it faster to execute tests compared to Jest.
- Configuration: Configuration happens using a
vite.config
file, which can also be used to configure other aspects of your project. This means you can configure both your code, and your test suite from a single source. - Supports: It supports TypeScript and JSX out of the box. It also supports using import statements, which is not something that you can do in Jest out of the box. It also supports top-level await, as well as code coverage which we will look into how to do.
Writing Your First Unit Tests Using Vitest
To get up and running with Vitest, you first need to have a Vite project. In case you already have one, you can jump to the next section. In case you don't have one, you can create a new one using the following command:
npm create vite .
This will initiate a new Vite project in the current directory. You also have the option to choose the following frameworks when bootstrapping Vite:
> vanilla
- vue
- react
- preact
- lit
- svelte
In this tutorial, we are going to go with a vanilla project. During the setup, you can also choose to set up TypeScript along with your Vite project. After your project is bootstrapped, don't forget to run npm i
to install all dependencies.
Installing and using Vitest
Once you have a Vite project ready, you will also need to install Vitest as a dev dependency.
npm i --save-dev vitest
Notice that Vitest is still under version 1.x. This means breaking changes might be expected.
Now that we have all the required dependencies installed, it's time to create a test file to start writing some unit test cases. Create a new test file somewhere in your project's folder and add the following:
import sum from './sum'
import { describe, it, expect } from 'vitest'
describe('sum', () => {
it('Should sum two numbers together', () => {
expect(sum(3, 3)).toBe(6)
})
})
The very first thing you may notice is that in Vitest, we need to import the different building blocks of a unit test. Other than that, the syntax is more or less the same as Jest.
To run the test, open your package.json
file and create a new test
command in your scripts
object.
{
"scripts": {
...
"test": "vitest"
}
}
Now you can call npm run test
to execute Vitest to run your tests. This will run Vitests in watch mode, meaning you can make some changes to your unit tests, save the file, and Vitest will rerun your test suite.
The good thing? If you have multiple test files (which you will likely have), Vitest will only execute the file that is changed, so no need to wait for all the other tests to pass. To perform a single run without watch mode, you can change your test
script to call vitest run
.
{
"scripts": {
...
"test": "vitest run"
}
}
Apart from the Jest compatible APIs, Vitest also has Chai built-in for assertions, as well as jsdom for DOM mocking. All of this makes it super easy to migrate your test suite from Jest to Vitest.
In-source Testing
Another great feature of Vitest is in-source testing. In-source testing lets you write your test cases right next to your functions. Let's see how the above example can be translated to in-source testing.
First, you will need to move your unit test underneath your exported function, and wrap it in the following if
statement:
import { describe, it, expect } from 'vitest'
export const sum = (a: number, b: number): number => a + b
if (import.meta.vitest) {
describe('sum', () => {
it('Should sum two numbers together', () => {
expect(sum(2, 2)).toBe(4)
})
})
}
This check will tell Vite to only run the code if we are executing Vitest. So the part inside the if
statement will only be executed if we are running the code from the unit test suite. Now using this approach we will run into three problems:
- As this is not a test file, Vitest will be unable to find the test
- The code will be bundled into the final app
- If you are using TypeScript, you will get a type error for
meta.vitest
Luckily, all three of them can be resolved relatively easily. To let Vitest find the test, we need to modify our vite.config
file. Inside your vite.config
, add the following to your defineConfig
call.
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
includeSource: ['**/*.ts']
}
})
This will tell Vitest to look for tests in files ending with .ts
. By default, it will only look for tests in files with a .test.extension
or .spec.extension
ending. To ensure that the test code is not included in the final bundle, we can extend the config with the following lines:
import { defineConfig } from 'vitest/config'
export default defineConfig({
+ define: {
+ 'import.meta.vitest': 'undefined'
+ },
test: {
includeSource: ['**/*.ts']
}
})
This will ensure that import.meta.vitest
will always be skipped for production builds. Last but not least, to fix type errors related to import.meta.vitest
, we need to modify tsconfig.json
with an extended type.
"types": [
"vitest/importMeta"
]
Test Coverage Reports Using Vitest
Vitest supports generating test coverage out of the box. It requires c8 as a dependency, so in case you don't have it installed, run npm i --save-dev c8
. Vitest will also prompt you to install it during the first coverage run. To create a coverage report, run vitest --coverage
. This will generate a report into your terminal.
In case you want to work with a coverage report represented by HTML, you can add the following config to your vite.config
file.
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
coverage: {
reporter: ['text', 'html']
}
}
})
This will generate a coverage
folder at the root of your project, where you can find an index.html
report file for your code coverage, that is generated using Istanbul.
Summary
In summary, Vitest provides various improvements on top of Jest. Its similar API to Jest makes it very easy to migrate your unit tests. Its single configuration file lets you configure both your app and your unit test suite from one place. Its implementation lets you execute your test suite faster compared to Jest.
Have you already worked with Vitest before? Let us know your thoughts on it in the comments below! Want to learn more about unit testing? Check out our tutorials below on how to achieve various test scenarios in Jest. Thank you for reading through, happy testing!
Rocket Launch Your Career
Speed up your learning progress with our mentorship program. Join as a mentee to unlock the full potential of Webtips and get a personalized learning experience by experts to master the following frontend technologies: