进阶 vitest.dev 2026-10-22 19:00:41 · 18 阅读

第15章 Vitest 测试项目配置指南

测试项目 示例项目 GitHub - 在线播放 警告 此功能也被称为 workspace。Workspace 自 3.2 版本起已弃用,并由 projects 配置取代。两者在功能上是相同的。

Vitest 提供了一种在单个 Vitest 进程中定义多个项目配置的方法。该功能在 monorepo 配置中特别有用,也可用于以不同的配置运行测试,例如 resolve.alias、plugins 或 test.browser 等。

定义项目 你可以在根配置中定义项目: vitest.config.ts ts import { defineConfig } from 'vitest/config'

export default defineConfig({ test: { projects: ['packages/*'], }, })

项目配置是内联配置、文件或引用你项目的 glob 模式。例如,如果你有一个名为 packages 的文件夹,其中包含你的项目,你可以在根 Vitest 配置中定义一个数组: vitest.config.ts ts import { defineConfig } from 'vitest/config'

export default defineConfig({ test: { projects: ['packages/*'], }, })

即使 packages 内的某个文件夹没有内部配置文件,Vitest 也会将其视为独立的项目。如果项目条目解析为一个文件(无论是通过 glob 模式还是直接文件路径),Vitest 都会验证该文件的名称是否符合以下规则: 以 vitest.config 或 vite.config 开头(例如,vitest.config.unit.ts) 或者匹配 vitest..config.* / vite..config.*,其中 可以包含字母、数字、_ 和 -

例如,以下配置文件是有效的: vitest.config.ts vite.config.js vitest.unit.config.ts vitest.e2e-node.config.ts vite.e2e.config.js vitest.config.unit.js vite.config.e2e.js

若要排除特定文件夹和文件,可以使用否定模式: vitest.config.ts ts import { defineConfig } from 'vitest/config'

export default defineConfig({ test: { // 包含 "packages" 内的所有文件夹,除了 "excluded" projects: [ 'packages/*', '!packages/excluded' ], }, })

如果你有嵌套结构,其中一些文件夹需要作为项目,而其他文件夹包含子文件夹,你必须使用方括号来避免匹配父文件夹: vitest.config.ts ts import { defineConfig } from 'vitest/config'

// 例如,这将创建以下项目: // packages/a // packages/b // packages/business/c // packages/business/d // 注意 "packages/business" 本身不是一个项目

export default defineConfig({ test: { projects: [ // 匹配 "packages" 内的所有文件夹,除了 "business" 'packages/!(business)', // 匹配 "packages/business" 内的所有文件夹 'packages/business/*', ], }, })

警告 Vitest 不会将根 vitest.config 文件视为项目,除非在配置中明确指定。因此,根配置仅影响全局选项,如 reporters 和 coverage。请注意,Vitest 始终会运行在根配置文件中指定的某些插件钩子,如 apply、config、configResolved 或 configureServer。Vitest 还使用相同的插件来执行全局设置和自定义覆盖率提供者。

你还可以通过配置文件引用项目: vitest.config.ts ts import { defineConfig } from 'vitest/config'

export default defineConfig({ test: { projects: ['packages/*/vitest.config.{e2e,unit}.ts'], }, })

此模式仅包含在文件扩展名前含有 e2e 或 unit 的 vitest.config 文件所对应的项目。

你也可以使用内联配置来定义项目。该配置同时支持两种语法。 vitest.config.ts ts import { defineConfig } from 'vitest/config'

export default defineConfig({ test: { projects: [ // 匹配 `packages` 文件夹内的所有文件夹和文件 'packages/*', { // 内联项目默认继承 // 此配置文件的选项 test: { include: ['tests//*.{browser}.test.{ts,js}'], // 使用内联配置时建议定义一个名称 name: 'happy-dom', environment: 'happy-dom', } }, { // 添加 "extends: false" 以忽略 // 此配置文件中定义的选项 extends: false, test: { include: ['tests//*.{node}.test.{ts,js}'], // 可以更改名称标签的颜色 name: { label: 'node', color: 'green' }, environment: 'node', } } ] } })

警告 所有项目必须具有唯一的名称;否则,Vitest 将抛出错误。如果内联配置中未提供名称,Vitest 将分配一个数字。对于使用 glob 语法定义的项目配置,Vitest 默认使用最近的 package.json 文件中的 "name" 属性,或者如果不存在,则使用文件夹名称。

项目不支持所有配置属性。为了获得更好的类型安全性,在项目配置文件中请使用 defineProject 方法代替 defineConfig: packages/a/vitest.config.ts ts import { defineProject } from 'vitest/config'

export default defineProject({ test: { environment: 'jsdom', // "reporters" 在项目配置中不受支持, // 因此会显示错误 reporters: ['json']

No overload matches this call. The last overload gave the following error. Object literal may only specify known properties, and 'reporters' does not exist in type 'Omit & { mode?: string | undefined; sequence?: (Omit & { ...; }) | undefined; deps?: DepsOptions | undefined; } & { ...; }'. } })

默认情况下,每个项目测试中的 process.cwd() 返回启动 Vitest 的目录,即使该项目的根目录不同。有关详细信息和解决方案,请参阅“项目工作目录不会改变”。

运行测试 要运行测试,请在根 package.json 中定义一个脚本: package.json json { "scripts": { "test": "vitest" } }

现在,你可以使用包管理器运行测试: npm yarn pnpm bun bash npm run test bash yarn test bash pnpm run test bash bun run test

如果你只需要在单个项目内运行测试,请使用 --project CLI 选项: npm yarn pnpm bun bash npm run test --project e2e bash yarn test --project e2e bash pnpm run test --project e2e bash bun run test --project e2e

提示 CLI 选项 --project 可以多次使用,以过滤多个项目: npm yarn pnpm bun bash npm run test --project e2e --project unit bash yarn test --project e2e --project unit bash pnpm run test --project e2e --project unit bash bun run test --project e2e --project unit

该过滤器支持 * 通配符和 ! 排除。如果一个项目不匹配任何否定模式,并且在提供常规模式时至少匹配其中一个模式,它就会运行: bash # 运行除 "e2e" 以外的所有项目 vitest --project '!e2e' # 运行所有以 "unit" 开头的项目,除了 "unit (browser)" vitest --project 'unit*' --project '!unit (browser)'

配置 使用内联配置定义的项目会继承根级配置的所有选项。这是由 extends 选项控制的,自 Vitest 5.0 起默认启用: vitest.config.ts ts import { defineConfig } from 'vitest/config' import react from '@vitejs/plugin-react'

export default defineConfig({ plugins: [react()], test: { pool: 'threads', projects: [ { // 继承此配置中的选项,如 plugins 和 pool // (`extends: true` 是默认值) test: { name: 'unit', include: ['/*.unit.test.ts'], }, }, { // 不继承此配置中的任何选项 extends: false, test: { name: 'integration', include: ['/*.integration.test.ts'], }, }, ], }, })

如果你想从根配置以外的配置文件中继承选项,extends 选项也接受另一个配置文件的路径: vitest.config.ts ts import { defineConfig } from 'vitest/config'

export default defineConfig({ test: { projects: [ { extends: './vitest.shared.ts', test: { name: 'unit', include: ['/*.unit.test.ts'], }, }, ], }, })

扩展配置中的所有选项都会与项目自身的选项合并。请注意,像 setupFiles 这样的数组是连接而非覆盖。几个选项有特殊处理: name 和 projects 永远不会被继承。 globalSetup 不会从根配置继承:根级别的 globalSetup 每次测试运行已经执行过一次,因此继承它会导致在每个项目中再次运行相同的文件。当扩展非根配置文件时,它仍然会被继承。 项目自身的 tags 会替换继承的数组,而不是与之合并。

如果你通过高级 API 运行 Vitest,请参阅“项目配置解析”了解程序化配置如何参与继承。

作为配置文件或目录引用的项目不会从根配置继承任何选项。你可以创建一个共享配置文件,并手动将其与项目配置合并: packages/a/vitest.config.ts ts import { defineProject, mergeConfig } from 'vitest/config' import configShared from '../vitest.shared.js'

export default mergeConfig( configShared, defineProject({ test: { environment: 'jsdom', } }) )

不支持的选项 某些配置选项不允许在项目配置中使用。值得注意的是: coverage:覆盖率为整个进程执行 reporters:仅支持根级别的 reporters resolveSnapshotPath:仅尊重根级别的解析器 attachmentsDir:附件存储在由所有项目共享的一个根级目录中 所有其他不影响测试运行器的选项

项目配置中不支持的所有配置选项在其名称旁都标有一个 图标。它们只能在根配置文件中定义一次。

嵌套项目 作为配置文件(或包含该文件的目录)引用的项目本身可以声明项目。这样的配置行为就像根配置一样:它本身不运行任何测试,仅提供运行测试的项目。这使得引用已定义自身项目的工作区成为可能: vitest.config.ts ts import { defineConfig } from 'vitest/config'

export default defineConfig({ test: { projects: ['./packages/app/vitest.config.ts'], }, })

packages/app/vitest.config.ts ts import { defineProject } from 'vitest/config'

export default defineProject({ test: { name: 'app', projects: [ { test: { name: 'unit', include: ['/*.unit.test.ts'], }, }, { test: { name: 'e2e', include: ['/*.e2e.test.ts'], }, }, ], }, })

嵌套项目的工作方式与在根配置中定义的项目相同:内联配置扩展声明它们的配置(这里是 app 配置,而不是根配置),extends 路径相对于该配置解析,并且其自身的 globalSetup 会被扩展项目继承,就像其他非根配置一样。

嵌套项目的名称前缀为声明它们的配置的名称,因此上述示例创建了 app (unit) 和 app (e2e) 项目。--project 过滤器也会匹配前缀:--project app 运行 app 配置中的所有项目,而 --project "app (unit)" 只运行其中一个。

为了也运行声明项目的配置的测试,请引用其自身的配置文件: packages/app/vitest.config.ts ts import { defineProject } from 'vitest/config'

export default defineProject({ test: { name: 'app', include: ['/*.test.ts'], projects: [ // "app" 项目将其自身的 "include" 与 "app (unit)" 一起运行 './vitest.config.ts', { test: { name: 'unit', include: ['/*.unit.test.ts'], }, }, ], }, })

请注意,只有配置文件可以定义嵌套项目。内联配置中的 projects 选项不受支持。

调试项目解析 如果项目未按预期解析,请设置 DEBUG=vitest:projects 环境变量运行 Vitest: bash DEBUG=vitest:projects vitest

Vitest 将记录每个项目是如何解析的:glob 模式匹配了哪些文件,浏览器实例和基准测试项目是如何扩展的,项目为何被 --project 过滤器丢弃,以及某个项目是创建自己的 Vite 服务器还是与其他项目共享一个服务器: vitest:projects resolving 3 project definitions declared by /vitest.config.ts vitest:projects projects glob "packages/*" matched 2 paths vitest:projects inline project "unit" shares the Vite server of /vitest.config.ts vitest:projects project "e2e" is dropped by the --project filter: unit vitest:projects resolved projects: "unit", "pkg-a", "pkg-b" vitest:projects creating a Vite server for project "pkg-a"

评论 (0)