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

第17章 并行与并发机制

并发 Vitest 拥有两级并发机制:它既能同时运行多个测试文件,也能在单个文件内同时运行多个测试。理解这两者的区别至关重要,因为它们的工作方式不同,权衡取舍也各异。

文件级并发 默认情况下,Vitest 会在多个 Worker(工作线程/进程)中并行运行测试文件。每个文件都拥有独立的隔离环境,因此不同文件中的测试互不干扰。

Vitest 创建 Worker 的机制取决于配置的 pool 类型: * forks(默认值)和 vmForks:每个文件运行在独立的子进程中 * threads 和 vmThreads:每个文件运行在独立的工作线程中

你可以使用 maxWorkers 选项来控制同时运行的 Worker 数量。Worker 数量越多,并行的文件就越多,但内存和 CPU 占用也越高。最佳数量取决于你的机器性能以及测试的复杂程度。

对于大多数项目而言,文件级并发是影响测试套件速度的最大单一因素。不过在某些情况下你可能需要禁用它——例如,如果你的测试共享了数据库等无法处理并发访问的外部资源。此时你可以将 fileParallelism 设置为 false,以按顺序逐个运行文件。

如需了解如何调优性能,请参阅性能指南。

测试级并发 在单个文件内,Vitest 默认按顺序运行测试。测试按照定义顺序依次执行。这是最安全的默认设置,因为同一文件中的测试通常通过 beforeEach 等生命周期钩子共享初始化和状态。

如果文件中的测试彼此独立,你可以使用 concurrent 修饰符选择并发运行:

```ts import { expect, test } from 'vitest'

test.concurrent('获取用户资料', async () => { const user = await fetchUser(1) expect(user.name).toBe('Alice') })

test.concurrent('获取用户帖子', async () => { const posts = await fetchPosts(1) expect(posts).toHaveLength(3) }) ```

当测试被标记为 concurrent 时,Vitest 会将它们分组并使用 Promise.all 运行。同时运行的测试数量受 maxConcurrency 选项限制。

何时 concurrent 真正有用? Vitest 不会为并发测试创建额外的 Worker——它们都在所属文件的同一 Worker 中运行。这意味着只有当你的测试花费时间在等待上(如网络请求、定时器、文件 I/O 等)时,concurrent 才能加速。纯同步测试不会受益,因为它们仍然阻塞单一的 JavaScript 线程:

```ts // 这些测试尽管标记了 `concurrent`,但仍是一个接一个运行, // 因为没有任何需要 await 的操作 test.concurrent('第一个测试', () => { expect(1).toBe(1) })

test.concurrent('第二个测试', () => { expect(2).toBe(2) }) ```

你也可以将 concurrent 应用于整个测试套件:

```ts import { describe, expect, test } from 'vitest'

describe.concurrent('用户 API', () => { test('获取资料', async () => { const user = await fetchUser(1) expect(user.name).toBe('Alice') })

test('获取帖子', async () => { const posts = await fetchPosts(1) expect(posts).toHaveLength(3) }) }) ```

如果你希望项目中所有测试默认并发运行,请在配置中将 sequence.concurrent 设置为 true。

你也可以通过设置 concurrent: false 让单个测试或套件退出继承的并发模式:

```ts test('使用共享资源', { concurrent: false }, async () => { // ... })

describe('共享资源套件', { concurrent: false }, () => { test('步骤 1', async () => { /* ... */ }) test('步骤 2', async () => { /* ... */ }) }) ```

并发测试中的钩子 当测试并发运行时,生命周期钩子的行为会发生变化。beforeAll 和 afterAll 仍然对整组只执行一次,但 beforeEach 和 afterEach 会为每个测试执行——甚至可能同时执行,因为测试本身是重叠运行的。

钩子的执行顺序由 sequence.hooks 控制。当 sequence.hooks 设置为 'parallel' 时,钩子也会受到 maxConcurrency 限制。

评论 (0)