Vitest
Vitest 是来自 Vite 生态系统的测试运行器。将其与 Turborepo 集成将带来巨大的速度提升。
¥Vitest is a test runner from the Vite ecosystem. Integrating it with Turborepo will lead to enormous speed-ups.
Vitest 文档 展示了如何创建一个 "Vitest 项目" 配置,该配置可以通过一个根命令运行 monorepo 中的所有测试,从而实现开箱即用的合并覆盖率报告等功能。此功能不符合 monorepo 的现代最佳实践,因为它的设计初衷是与 Jest 兼容(Jest 的工作区功能是在 软件包管理器工作区 之前构建的)。
¥The Vitest documentation shows how to create a "Vitest Projects" configuration 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).
Vitest 已弃用工作区,转而使用项目。使用项目时,单个项目的 vitest 配置无法再扩展根配置,因为它们会继承项目的配置。相反,需要一个像 vitest.shared.ts
这样的单独共享文件。
¥Vitest has deprecated workspaces in favor of projects. When using projects, individual project vitest configs can't extend the root config anymore since they would inherit the projects configuration. Instead, a separate shared file like vitest.shared.ts
is needed.
因此,你有两个选项,每个选项都有各自的优缺点:
¥Because of this you have two options, each with their own tradeoffs:
利用 Turborepo 进行缓存
¥Leveraging Turborepo for caching
为了提高缓存命中率并仅运行包含更改的测试,你可以选择为每个包配置任务,将 Vitest 命令拆分为每个包中单独的可缓存脚本。这种速度的代价是你需要自己创建合并的覆盖率报告。
¥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.
完整示例:运行 npx create-turbo@latest --example with-vitest
或 访问示例源代码。
¥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:
apps/web
和 packages/ui
都有自己的测试套件,vitest
和 安装到使用它们的包中 分别是测试套件。他们的 package.json
文件包含一个运行 Vitest 的 test
脚本:
¥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:
在根 turbo.json
文件中,创建一个 test
任务:
¥Inside the root turbo.json
, create a test
task:


现在,turbo run test
可以并行化和缓存每个包中的所有测试套件,只测试已更改的代码。
¥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
当你在 CI 中运行测试套件时,它会记录结果并在完成后最终退出。这意味着你可以进行 使用 Turborepo 缓存。但是,当你在开发过程中使用 Vitest 的监视模式运行测试时,该进程永远不会退出。这使得监视任务更像 长期运行的开发任务。
¥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.
由于这种差异,我们建议指定两个单独的 Turborepo 任务:一个用于运行测试,另一个用于在监视模式下运行测试。
¥Because of this difference, we recommend specifying two separate Turborepo tasks: one for running your tests, and one for running them in watch mode.
以下策略创建了两个任务,一个用于本地开发,一个用于持续集成 (CI)。你可以选择将 test
任务用于本地开发,并创建一些 test:ci
任务。
¥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.
例如,在每个工作区的 package.json
文件中:
¥For example, inside the package.json
file for each workspace:
并且,在根 turbo.json
中:
¥And, inside the root turbo.json
:


你现在可以将 全局 turbo
用作 turbo run test:watch
或从根目录 package.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 的项目功能 会创建一个开箱即用的覆盖率报告,该报告会合并你所有软件包的测试覆盖率报告。但是,按照 Turborepo 的策略,你必须自行合并覆盖率报告。
¥Vitest's Projects 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.
with-vitest
示例 展示了一个完整的示例,你可以根据自己的需求进行调整。你可以使用 npx create-turbo@latest --example with-vitest
快速上手。
¥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:
-
运行
turbo run test
以创建覆盖率报告。¥Run
turbo run test
to create the coverage reports. -
将覆盖率报告与
nyc merge
合并。¥Merge the coverage reports with
nyc merge
. -
使用
nyc report
创建报告。¥Create a report using
nyc report
.
Turborepo 要完成的任务如下所示:
¥Turborepo tasks to accomplish will look like:


完成此配置后,运行 turbo test && turbo report
以创建合并的覆盖率报告。
¥With this in place, run turbo test && turbo report
to create a merged coverage report.
with-vitest
示例 展示了一个完整的示例,你可以根据自己的需求进行调整。你可以使用 npx create-turbo@latest --example with-vitest
快速上手。
¥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
.
使用 Vitest 的项目功能
¥Using Vitest's Projects feature
Vitest 项目功能与 软件包管理器工作区 的模型不同。相反,它使用一个根脚本,然后深入到存储库中的每个软件包中,处理相应软件包中的测试。
¥The Vitest Projects 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.
从现代 JavaScript 生态系统的角度来看,此模型中不存在包边界。这意味着你不能依赖 Turborepo 的缓存,因为 Turborepo 依赖于这些包边界。
¥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.
因此,如果你想使用 Turborepo 运行测试,则需要使用 根任务。配置好 Vitest 项目设置 后,为 Turborepo 创建根任务:
¥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 Projects setup, 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
你可以通过实现混合解决方案来结合两种方法的优势。此方法使用 Vitest 的项目功能统一本地开发,同时在 CI 中保留 Turborepo 的缓存。这需要付出一些代价,即配置略多,并且存储库中的任务运行模型会比较复杂。
¥You can combine the benefits of both approaches by implementing a hybrid solution. This approach unifies local development using Vitest's Projects feature 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.
首先,创建一个共享配置包,因为在使用项目时,单个项目无法扩展根配置。为你的共享 Vitest 配置创建一个新包:
¥First, create a shared configuration package since individual projects can't extend the root config when using projects. Create a new package for your shared Vitest configuration:
然后,使用项目创建你的 Vitest 根配置:
¥Then, create your root Vitest configuration using projects:
在此设置中,你的包将维护各自的 Vitest 配置,并导入共享配置。首先,安装共享配置包:
¥In this setup, your packages maintain their individual Vitest configurations that import the shared config. First, install the shared config package:
然后创建 Vitest 配置:
¥Then create the Vitest configuration:
确保更新 turbo.json
,将新的配置包包含在依赖图中:
¥Make sure to update your turbo.json
to include the new configuration package in the dependency graph:


虽然你的根 package.json
包含用于全局运行测试的脚本:
¥While your root package.json
includes scripts for running tests globally:
此配置允许开发者在根目录下运行 pnpm test:projects
或 pnpm test:projects:watch
,以便在使用 Vitest 项目时获得无缝的本地开发体验,而 CI 则继续使用 turbo run test
来利用包级缓存。你仍然需要按照上一节中的说明手动处理合并的覆盖率报告。
¥This configuration allows developers to run pnpm test:projects
or pnpm test:projects:watch
at the root for a seamless local development experience using Vitest projects, 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.