Turborepo logo

Vitest

Vitest 是 Vite 生态系统中的一个测试运行器。将其与 Turborepo 集成将大幅提升速度。

Vitest 文档 展示了如何创建一个“Vitest 工作区”,该工作区从一个根命令运行 monorepo 中的所有测试,从而实现开箱即用的合并覆盖率报告等行为。此功能不遵循现代 monorepo 的最佳实践,因为它专为与 Jest 兼容而设计(Jest 的工作区功能在 包管理器工作区 之前构建)。

因此,您有两种选择,每种选择都有其自身的权衡

利用 Turborepo 进行缓存

为了提高缓存命中率并仅运行有更改的测试,您可以选择为每个包配置任务,将 Vitest 命令拆分为每个包中单独的、可缓存的脚本。这种速度提升的代价是您需要自己创建合并的覆盖率报告。

对于一个完整的示例,运行 npx create-turbo@latest --example with-vitest访问示例的源代码

设置

假设我们有一个简单的 包管理器工作区,如下所示

package.json
package.json

apps/webpackages/ui 都有自己的测试套件,其中 vitest 安装到使用它们的包中。它们的 package.json 文件包含一个运行 Vitest 的 test 脚本

./apps/web/package.json
{
  "scripts": {
    "test": "vitest run"
  },
  "devDependencies": {
    "vitest": "latest"
  }
}

在根目录 turbo.json 中,创建一个 test 任务

./turbo.json
{
  "tasks": {
    "test": {
      "dependsOn": ["transit"]
    },
    "transit": {
      "dependsOn": ["^transit"]
    }
  }
}

现在,turbo run test 可以并行化和缓存来自每个包的所有测试套件,仅测试已更改的代码。

在监听模式下运行测试

当您在 CI 中运行测试套件时,它会记录结果并在完成后最终退出。这意味着您可以使用 Turborepo 缓存它。但是当您在开发期间使用 Vitest 的监听模式运行测试时,该进程永远不会退出。这使得监听任务更像是一个 长时间运行的开发任务

由于这种差异,我们建议指定两个单独的 Turborepo 任务:一个用于运行测试,另一个用于在监听模式下运行它们。

下面的策略创建了两个任务,一个用于本地开发,一个用于 CI。您可以选择将 test 任务用于本地开发,并创建一些 test:ci 任务来代替。

例如,在每个工作区的 package.json 文件中

./apps/web/package.json
{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest --watch"
  }
}

并且,在根目录 turbo.json

./turbo.json
{
  "tasks": {
    "test": {
      "dependsOn": ["^test"]
    },
    "test:watch": {
      "cache": false,
      "persistent": true
    }
  }
}

您现在可以使用 全局 turbo 作为 turbo run test:watch 运行您的任务,或者从根目录 package.json 中的脚本运行

终端
turbo run test
 
turbo run test:watch

创建合并的覆盖率报告

Vitest 的工作区功能 创建了一个开箱即用的覆盖率报告,该报告合并了所有包的测试覆盖率报告。但是,按照 Turborepo 策略,您必须自己合并覆盖率报告。

with-vitest 示例 展示了一个完整的示例,您可以根据自己的需求进行调整。您可以使用 npx create-turbo@latest --example with-vitest 快速开始使用它。

为此,您将遵循几个 सामान्य 步骤

  1. 运行 turbo run test 以创建覆盖率报告。
  2. 使用 nyc merge 合并覆盖率报告。
  3. 使用 nyc report 创建报告。

要完成的 Turborepo 任务将如下所示

./turbo.json
{
  "tasks": {
    "test": {
      "dependsOn": ["^test", "@repo/vitest-config#build"],
      "outputs": ["coverage.json"]
    }
    "merge-json-reports": {
      "inputs": ["coverage/raw/**"],
      "outputs": ["coverage/merged/**"]
    },
    "report": {
      "dependsOn": ["merge-json-reports"],
      "inputs": ["coverage/merge"],
      "outputs": ["coverage/report/**"]
    },
  }
}

完成这些设置后,运行 turbo test && turbo report 以创建合并的覆盖率报告。

with-vitest 示例 展示了一个完整的示例,您可以根据自己的需求进行调整。您可以使用 npx create-turbo@latest --example with-vitest 快速开始使用它。

使用 Vitest 的工作区功能

Vitest 工作区功能不遵循与 包管理器工作区 相同的模型。相反,它使用一个根脚本,然后扩展到仓库中的每个包以处理该相应包中的测试。

在这种模型中,从现代 JavaScript 生态系统的角度来看,没有包边界。这意味着您不能依赖 Turborepo 的缓存,因为 Turborepo 依赖于这些包边界。

因此,如果您想使用 Turborepo 运行测试,则需要使用 根任务。配置 Vitest 工作区 后,为 Turborepo 创建根任务

./turbo.json
{
  "tasks": {
    "//#test": {
      "outputs": ["coverage/**"]
    },
    "//#test:watch": {
      "cache": false,
      "persistent": true
    }
  }
}

值得注意的是,根任务的文件输入默认包含所有包,因此任何包中的任何更改都会导致缓存未命中。 虽然这确实简化了创建合并覆盖率报告的配置,但您将错过缓存重复工作的机会。

使用混合方法

您可以通过实施混合解决方案来结合两种方法的优点。这种方法统一了使用 Vitest 工作区方法进行本地开发,同时在 CI 中保留了 Turborepo 的缓存。这样做需要在仓库中进行稍微更多的配置和混合的任务运行模型之间进行权衡。

./vitest.workspace.ts
import { defineWorkspace } from 'vitest/config';
 
export default defineWorkspace(['packages/*']);

在此设置中,您的包维护其各自的 Vitest 配置和脚本

./packages/ui/package.json
{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest --watch"
  }
}

而您的根目录 package.json 包含用于全局运行测试的脚本

./package.json
{
  "scripts": {
    "test:workspace": "turbo run test",
    "test:workspace:watch": "vitest --watch"
  }
}

此配置允许开发人员在根目录运行 pnpm test:workspace:watch 以获得无缝的本地开发体验,而 CI 继续使用 turbo run test 来利用包级别的缓存。您仍然需要按照上一节中的描述手动处理合并的覆盖率报告

小时

总计算节省
开始使用
远程缓存 →

本页内容