Vitest
Vitest is a test runner from the Vite ecosystem. Integrating it with Turborepo will lead to enormous speed-ups.
The Vitest documentation shows how to create a "Vitest Workspace" that runs all tests in the monorepo from one root command, enabling behavior like merged coverage reports out-of-the-box. This feature doesn't follow modern best practices for monorepos, since its designed for compatibility with Jest (whose Workspace feature was built before package manager Workspaces).
Because of this you have two options, each with their own tradeoffs:
Leveraging Turborepo for caching
To improve on cache hit rates and only run tests with changes, you can choose to configure tasks per-package, splitting up the Vitest command into separate, cacheable scripts in each package. This speed comes with the tradeoff that you'll need to create merged coverage reports yourself.
For a complete example, run npx create-turbo@latest --example with-vitest
or
visit the example's source
code.
Setting up
Let's say we have a simple package manager Workspace that looks like this:
Both apps/web
and packages/ui
have their own test suites, with vitest
installed into the packages that use them. Their package.json
files include a test
script that runs Vitest:
Inside the root turbo.json
, create a test
task:


Now, turbo run test
can parallelize and cache all of the test suites from each package, only testing code that has changed.
Running tests in watch mode
When you run your test suite in CI, it logs results and eventually exits upon completion. This means you can cache it with Turborepo. But when you run your tests using Vitest's watch mode during development, the process never exits. This makes a watch task more like a long-running, development task.
Because of this difference, we recommend specifying two separate Turborepo tasks: one for running your tests, and one for running them in watch mode.
This strategy below creates two tasks, one for local development and one for
CI. You could choose to make the test
task for local development and create
some test:ci
task instead.
For example, inside the package.json
file for each workspace:
And, inside the root turbo.json
:


You can now run your tasks using global turbo
as turbo run test:watch
or from a script in your root package.json
:
Creating merged coverage reports
Vitest's Workspace feature creates an out-of-the-box coverage report that merges all of your packages' tests coverage reports. Following the Turborepo strategy, though, you'll have to merge the coverage reports yourself.
The with-vitest
example
shows a complete example that you may adapt for your needs. You can get
started with it quickly using npx create-turbo@latest --example with-vitest
.
To do this, you'll follow a few general steps:
- Run
turbo run test
to create the coverage reports. - Merge the coverage reports with
nyc merge
. - Create a report using
nyc report
.
Turborepo tasks to accomplish will look like:


With this in place, run turbo test && turbo report
to create a merged coverage report.
The with-vitest
example
shows a complete example that you may adapt for your needs. You can get
started with it quickly using npx create-turbo@latest --example with-vitest
.
Using Vitest's Workspace feature
The Vitest Workspace feature doesn't follow the same model as a package manager Workspace. Instead, it uses a root script that then reaches out into each package in the repository to handle the tests in that respective package.
In this model, there aren't package boundaries, from a modern JavaScript ecosystem perspective. This means you can't rely on Turborepo's caching, since Turborepo leans on those package boundaries.
Because of this, you'll need to use Root Tasks if you want to run the tests using Turborepo. Once you've configured a Vitest Workspace, create the Root Tasks for Turborepo:


Notably, the file inputs for a Root Task include all packages by default, so any change in any package will result in a cache miss. While this does make for a simplified configuration to create merged coverage reports, you'll be missing out on opportunities to cache repeated work.
Using a hybrid approach
You can combine the benefits of both approaches by implementing a hybrid solution.This approach unifies local development using Vitest's Workspace approach while preserving Turborepo's caching in CI. This comes at the tradeoff of slightly more configuration and a mixed task running model in the repository.
In this setup, your packages maintain their individual Vitest configurations and scripts:
While your root package.json
includes scripts for running tests globally:
This configuration allows developers to run pnpm test:workspace:watch
at the root for a seamless local development experience, while CI continues to use turbo run test
to leverage package-level caching. You'll still need to handle merged coverage reports manually as described in the previous section.