Vitest passWithNoTests 配置完全指南:让“零测试文件“不再导致 CI 失败 📅 发布时间:2026/9/14 2:11:25 👁 浏览次数: Vitest passWithNoTests 配置完全指南让零测试文件不再导致 CI 失败【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitestVitest 在默认情况下只要一轮运行中没有找到任何测试文件或测试用例就会以退出码1结束进程从而让 CI 流水线报红。passWithNoTests配置项正是为解决这一场景而生开启后Vitest 即使一个测试都没找到也会以退出码0正常通过。本文将基于 Vitest 仓库源码与官方配置文档系统讲解该配置的类型、CLI 用法、底层判定逻辑以及与--changed、--related、--shard等高级场景的联动行为帮助你彻底掌握无测试不失败的正确打开方式。配置项速览官方配置参考文档 docs/config/passwithnotests.md 给出的核心定义如下属性值TypebooleanDefaultfalseCLI--passWithNoTests、--passWithNoTestsfalse其行为一句话概括当本次运行没有找到任何测试时Vitest 不会失败不报错、退出码为 0。在 类型定义 中它被声明为一个可选的布尔字段/** * Pass with no tests */ passWithNoTests?: booleanCLI 侧的描述则被定义为Pass when no tests are found见 cli-config.ts与官方文档语义完全一致。什么时候会触发无测试失败要理解passWithNoTests的价值首先需要知道 Vitest 在哪些场景下会因为没有测试而失败。综合源码与测试主要有三类全局没有任何测试文件被解析到例如 include 模式匹配不到任何文件或文件被 filter 过滤为空某个测试文件里没有任何测试套件file 级任务列表为空某个测试套件里没有任何测试用例suite 级任务列表为空。这三种情况分别对应test-run.ts、run.ts中的三处独立校验详见下文源码级解析。只要没有开启passWithNoTestsVitest 就会为它们生成No test found in suite ...、No test suite found in file ...之类的失败结果并最终以非零退出码结束。一个非常典型的落地场景是临时清空/过滤测试当你用-t过滤用例、用 include 收紧匹配范围或暂时删掉所有用例只留空文件时CI 会在默认配置下直接失败而这往往并不是你真正想要的测试失败。开启passWithNoTests后这类无测试可跑的情况会被视为正常。配置方式方式一配置文件在vitest.config.ts中直接设置import { defineConfig } from vitest/config export default defineConfig({ test: { passWithNoTests: true, }, })方式二CLI 参数# 单次运行生效 vitest run --passWithNoTests # 显式关闭当配置文件里开了 true 时用它在命令行覆盖 vitest run --passWithNoTestsfalse注意 CLI 支持正反两种写法--passWithNoTests等价于true--passWithNoTestsfalse用于在命令行强制覆盖配置文件中已开启的值。这个布尔开关在 CLI 解析层cac.ts与配置解析层resolveConfig.ts都会参与合并最终值会被序列化进运行配置见 serializeConfig.ts。验证方式仓库自带的端到端测试 test/e2e/test/config/passWithNoTests.test.ts 直接验证了这一行为it(vitest doesnt fail when running empty files, async () { const { exitCode } await runInlineTests( { empty.test.js: }, { passWithNoTests: true }, ) expect(exitCode).toBe(0) })即一个内容为空的测试文件配合passWithNoTests: true最终退出码为0。源码级解析退出码判定链passWithNoTests的实现横跨主进程判定与运行时判定两层下面分别说明。主进程层hasFailed判定在主进程的 test-run.ts 中hasFailed方法的开头就是关键逻辑private hasFailed(modules: TestModule[]) { if (!modules.length) { return !this.vitest.config.passWithNoTests } return modules.some(m !m.ok()) }当modules.length 0即本次运行没有收集到任何测试模块时是否判定失败完全取决于passWithNoTests而当存在测试模块时失败与否仍由各模块自身的执行结果m.ok()决定——这说明passWithNoTests只豁免无测试这一种失败绝不掩盖真实用例的失败。Logger 层提示信息与退出码在 logger.ts 的printNoTestFound中可以看到无测试时的两种分支输出if (config.passWithNoTests) { this.log(No test files found, exiting with code 0\n) } else { this.error( c.red(No test files found, exiting with code 1\n), ) }同一场景下true与false会分别打印exiting with code 0/exiting with code 1的提示开发者可以据此在 CI 日志中快速确认是哪种分支生效。运行时层空套件与空文件在 worker 进程的运行时执行器 run.ts 中有两处与passWithNoTests直接相关的豁免套件级约 L952-L961当suite.mode为run/queued且suite.containsTest为假时若未开启该配置则把套件标记为失败并注入No test found in suite ${suite.name}错误文件级约 L1015-L1026当文件任务列表为空且未开启该配置时标记No test suite found in file ${file.filepath}失败。两处都使用!runner.config.passWithNoTests作为失败条件true时直接跳过失败标记。自动启用的两种场景--changed与vitest related这是很容易被忽视但非常实用的行为在部分场景下 Vitest 会替你自动开启passWithNoTests无需手动配置。在 cac.ts 中可以看到vitest related命令会显式兜底if (arrayArgs[2] related) { options.related args options.passWithNoTests ?? true args [] } // ... async function runRelated(relatedFiles: string[] | string, argv: CliOptions): Promisevoid { argv.related relatedFiles argv.passWithNoTests ?? true await start([], argv) }同样在配置解析层 resolveConfig.ts 中--changed模式也会自动兜底if (resolved.changed) { resolved.passWithNoTests ?? true }注意这里使用的是??空值合并赋值即仅在用户没有显式设置过该值时才自动置为true。如果你显式写了--passWithNoTestsfalse你的显式值仍会生效。原因很直观--changed和vitest related分别基于 git 变更文件与传入的相关文件列表来筛选测试。当这次变更恰好没有触及任何测试文件例如只改了文档、配置或一个未被测试覆盖的模块时找不到测试是完全正常的预期行为不该让 CI 失败。与其他特性的联动与--shard的联动在使用分片shard时passWithNoTests还有一个额外作用。在 pool.ts 中if (ctx.config.shard) { if (!ctx.config.passWithNoTests ctx.config.shard.count specs.length) { throw new Error( --shard count must be a smaller than count of test files. Resolved ${specs.length} test files for --shard${ctx.config.shard.index}/${ctx.config.shard.count}., ) } specs await sequencer.shard(Array.from(specs)) }默认情况下如果 shard 总数大于实际解析到的测试文件数例如配置--shard1/4但只有 2 个测试文件Vitest 会直接抛出错误而开启passWithNoTests后这一限制被放宽允许分片数多于文件数这种在动态仓库中可能出现的情况。对应的端到端测试见 test/e2e/test/failures.test.ts#L66-L67test(shard count can be smaller than count of test files when passWithNoTests, async () { const { stderr } await runVitest({ root: ./fixtures/shard, shard: 1/4, passWithNoTests: true, include: [**/*.test.js] }) })与 JSON Reporter 的联动JSON 报告器的success字段同样考虑了该配置。在 json.ts 中const success !!(files.length 0 || this.ctx.config.passWithNoTests) numFailedTestSuites 0 numFailedTests 0也就是说当没有任何文件被报告且未开启passWithNoTests时JSON 报告会被标记为失败开启后则仅由真实的失败套件/用例数决定。这保证了无测试场景下 JSON 结果与进程退出码的语义一致。使用建议与注意事项它只豁免无测试不豁免测试失败只要收集到了测试且其中有失败无论该配置是true还是false进程都会以非零码退出。不要把它当作跳过失败的开关。默认值保持false是有意为之对于常规测试仓库找不到测试往往意味着 include 模式写错或文件被误删此时快速失败反而有助于尽早暴露问题。建议仅在确实存在合法空跑场景时开启。优先依赖自动兜底对于基于 git 变更的--changed和vitest related工作流Vitest 已自动开启该行为通常无需手动配置而全量vitest run若需要空跑通过则应显式开启。CI 场景常见组合在 monorepo 中按包执行测试或按变更集增量执行时不同包/不同提交的测试数量天然不稳定此时配合passWithNoTests可以让流水线对本次没有可测内容保持稳定绿。小结passWithNoTests是 Vitest 中一个语义清晰但影响面广泛的布尔配置类型为boolean、默认false、支持--passWithNoTests/--passWithNoTestsfalse两种 CLI 写法。它贯穿了主进程的hasFailed判定、Logger 的退出码提示、运行时的空套件/空文件豁免并与--changed、vitest related、--shard、JSON 报告器产生联动。掌握它的完整行为边界能让你在增量测试、分片执行与 monorepo 场景下写出更健壮的测试脚本。【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitest创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考