第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.
例如,以下配置文件是有效的: 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
默认情况下,每个项目测试中的 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