Vite’s been taking the world by storm since early 2021. It dramatically simplified developer tooling and became the underpinning of several popular metaframeworks. Now, it’s ready to take over our test suites via Vitest. 😮
Let’s discuss how Vitest works, compare it with a popular test suite configuration (Jest and Babel), explore the ways Vitest can simplify your test suite config, and examine how the performance gains can speed up your watch
mode. If you’d like to jump around to sections of interest, click on the links below:
Vite’s value proposition has become so broad that it’s hard to pin down. To summarize: Vite is a website bundler that can handle your JavaScript, your CSS, your static assets, and just about anything you load into an HTML document.
Remember Apple’s original value proposition for the iPhone, “An iPod, a phone, an Internet communicator…”? I see Vite as a similar proposition, as it bundles three related tools into one:
How does Vite pull this off? Well, instead of targeting a single main.js
file as our website’s entry point, it crawls HTML files to track all JavaScript, styles, and miscellaneous assets you use. This enables Vite to manage the entire build pipeline, including a lightning-quick dev server. ⚡️
In development, Vite can intelligently decide what assets actually need to refresh when saving changes to a file. Edit a React component only used on your homepage, for example?
Instead of re-bundling your entire site as webpack might, Vite will send just the single React component over to your browser. It’ll even avoid refreshing the page and keep your state variables intact.
This hot module reloading is Vite’s secret sauce to a positive developer experience, which brings us to Vitest’s core value propositions.
🚨 Note: Vitest is still not recommended for production use as of February 2022. Check Vitest’s homepage for up-to-date suggestions before using it.
Vitest is a a Vi-testing framework built on top of Vite with an eye for both speed and minimal config. We’ll explore each of these in the following sections.
Vitest’s approach to the testing space is similar to Vite’s approach on the bundling space: let the tool control your entire environment, top to bottom. Vitest is a replacement for a suite of tools:
This means far less config, dependency mismatching, and performance monitoring for you to manage. Just install vitest
and let the magic happen. ✨
Despite Vitest’s sweeping value propositions, its test runner APIs are nearly identical to Jest across the board: describe
, expect
, it.each
, mock functions, spies, concurrent
flags for parallel tests… the gang’s all here (full list of APIs)!
In fact, Vitest and Jest are so similar that it’s hardly worth the code sample here. I suggest heading to Vitest’s playground to see the test runner in action. But for completeness, here’s a simple unit test written with Vitest’s APIs:
// example.test.js import { expect, test } from 'vitest'; test('Math.sqrt()', () => { expect(Math.sqrt(4)).toBe(2); expect(Math.sqrt(144)).toBe(12); expect(Math.sqrt(2)).toBe(Math.SQRT2); });
If you’re wondering, “Wait, why aren’t those test helpers globally available?” This is just the default. You can make the Vitest package globally available from a configuration file. See the Vitest docs for more information.
If you create this file locally, you can spin up Vitest’s watch
mode by running npx vitest
. You should see a single Math.sqrt()
test with three passing assertions.
Simple test cases are fine, but let’s explore some more complex use cases.
I created a full-stack web app using React for the frontend and a Netlify serverless function to handle form requests. I then wrote two test files:
You can explore the full GitHub repo here 👀
Note these each use a combination of not-so-stock tools: TypeScript, ESM for server and client-side logic, and JSX for the integration test. This should cover a spectrum of popular JavaScript use cases in a single project.
Let’s discuss the speed and config improvements using Jest + Babel as a baseline.
TL;DR: Vitest is much easier to configure from scratch than Jest and Babel. Using Vitest is almost a no-brainer if you use Vite already. Even if you don’t, there’s a minimal upfront cost to replicate your Babel build process in a Vite config. Still, if you want your config to be 100% identical to your web app’s, you might lean toward using Jest to piggyback on config.
Alright, let’s talk config. I tried to use the most popular test suite recommendations as a frame of reference: Jest for test utilities and Babel plugins to transpile what Jest doesn’t understand. I also avoided using Jest’s “experimental” flag for ESM because it’s rockier to use at this moment and may have performance implications.
Also, we’re configuring our test suite without considering the app itself. You might have a better time configuring Jest if your website already has a Babel and webpack config (say, CRA). Let’s ignore this for the sake of comparison, though.
Here are all the dev dependencies needed to get our tests running from absolute zero:
{ "devDependencies": { // babel presets for ESM, typescript, and React "babel-jest": "^27.5.0", "@babel/core": "^7.17.0", "@babel/preset-env": "^7.16.11", "@babel/preset-react": "^7.16.7", "@babel/preset-typescript": "^7.16.7", // Jest types for global "describe," "it," etc "@types/jest": "^27.4.0", // Helper to stub out CSS modules when present // per Jest docs recommendation // https://jestjs.io/docs/webpack#mocking-css-modules "identity-obj-proxy": "^3.0.0", "jest": "^27.5.0", "typescript": "^4.4.4" } }
That’s nine dependencies total. Despite using this combo of tools for years, it took nearly an hour of sifting through out-of-date packages and Stack Overflow posts to get here!
Stubbing out CSS modules was particularly thorny. Yes, your mileage will vary here, but I want to note this sticking point for newer devs, especially.
Now, let’s see our dependencies for Vitest:
{ "devDependencies": { "@testing-library/react": "^12.1.2", "typescript": "^4.4.4", "vitest": "^0.2.7", // optional plugin - auto-inject "React" import when it's missing "@vitejs/plugin-react": "^1.0.7", } }
We just went from using nine dependencies to four — three, excluding the optional React plugin. We also got it working on the first try from reading Vitest’s docs.
Now, if you’re thinking, “Well sure, but how much config did you have to write,” don’t worry. It gets better.
First, let’s see the necessary config for Jest plus Babel. We’ll need some Jest config to stub out CSS module imports:
// jest.config.js module.exports = { coverageProvider: "v8", moduleNameMapper: { // stub out CSS imports per Jest's recommendation "\\.(css)$": "identity-obj-proxy", }, };
As well as a few Babel presets for React and TypeScript:
// babel.config.js module.exports = { presets: [ ['@babel/preset-env', {targets: {node: 'current'}}], ['@babel/preset-react', { "runtime": "automatic" }], '@babel/preset-typescript', ], };
Now, let’s see the Vitest config. Because Vitest rolls up the testing and code bundling into a single tool, we only need one config file:
// vite.config.js import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], })
Look, there’s no config for TypeScript, CSS modules, environment presets, or the like! That’s because Vite handles this out of the box with sensible defaults that we don’t need to tweak. What’s more, because Vitest simply extends Vite’s capabilities, there’s zero necessary config for existing Vite projects.
I’ll add a disclaimer to this section before we begin: Vitest is still in its early days right now, so there are few benchmarks to speak of beyond this initial exploration from Matti Bar-Zeev. To summarize the findings:
watch
mode is much faster than the Jest equivalentMy findings were pretty dang similar.
I will say this is purely anecdotal evidence. These results are based on my local machine’s performance and will likely vary for those cloning the repo and trying at home. I recommend skeptical early adopters try for themselves, but here are my findings:
watch
mode — about 1s per run versus Vitest’s 1.88s using vitest run
.Let’s explore why this might be the case.
watch
mode so quick?I think Anthony Fu’s Twitter post says it best:
Just like how Vite works in the browser, Vitest also knows the graph of your modules, which makes it able to do smart detection and only rerun the related tests. Feels almost like HMR but for tests 😍
This is the greatest advantage of bundling your dev environment into a single tool. Because Vitest knows every module your app depends on, it can smartly decide which tests should rerun on a file change. This is especially useful in our integration test example.
Whenever we edit our Form.tsx
, Vitest can quickly discover which tests rely on this file, re-process it in a flash, and only rerun the related tests. By comparison, I couldn’t get Jest to rerun at all while editing test file dependencies like this.
Okay, let’s address that performance hit outside of watch
mode. This is a known issue in the Vitest community that stems from a core difference over Jest: Vitest is ESM-first, while Jest is based on the older CommonJS standard you may know from Node.
In other words, you can use import x from
'path'
instead of const x = require('path')
out of the box.
This is a huge win for modern developer tooling. As more and more modules are dropping CommonJS support, it’s crucial that our dev tools stay with the times.
However, this new ESM mindset forces Vitest to use a different test running strategy. To summarize:
This isolation means more time spent spinning up your test suites. Sadly, this has performance implications on small projects and at scale.
Note: you can disable this isolation from their CLI. You’ll need to be extra careful about global state shared between tests, but it should improve performance for most users.
But remember, Vitest is still in its early days. So, I’d avoid using Vitest for production apps right now, per their recommendation. Well, unless minimal config and fast dev servers are enough for you already. 😉
I hope this post gets you excited for Vitest’s future! It’s still in the development phase, so I highly encourage you to check out the Discord community to ask questions, contribute PRs, and watch the performance discussions over time. “Simplifying developer tooling” is a major trend to look out for, and I expect Vitest to only push this vision further.
There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.
LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.
LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.
Build confidently — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
3 Replies to "Testing Vite with minimal config using Vitest"
Note: Vitest is still not recommended for production use as of February 2022. Check Vitest’s homepage for up-to-date suggestions before using it.
I can’t find this content. please link
Can I use it in production now?
Ah, fair point! The warning banners have been removed from their docs looks like. Still, this is pre-1.0 software (beta coming soon) so I might steer clear for larger production projects. You can track their release notes (https://github.com/vitest-dev/vitest/releases) for when 1.0 is announced!
It seems like vitest have not only inherited jest’s API syntax but also the issue of not being able to mock CommonJS functions declared within the same module (https://github.com/vitest-dev/vitest/discussions/3667).
While with jest there is a workaround to use babel-plugin-rewire without altering the testing code (https://github.com/jestjs/jest/issues/936#issuecomment-545080082), could not get it to work with vitest so far.
And this issue is very critical for my tests.