Skip to content

分析测试性能

当你运行 Vitest 时,它会报告测试的多个时间指标:

bash
RUN  v2.1.1 /x/vitest/examples/profiling

 test/prime-number.test.ts (1) 4517ms
 generate prime number 4517ms

Test Files  1 passed (1)
     Tests  1 passed (1)
  Start at  09:32:53
  Duration  4.80s (transform 44ms, setup 0ms, import 35ms, tests 4.52s, environment 0ms)
  # 时间指标 ^^
  • Transform:花费在转换文件上的时间。参见 文件转换
  • Setup:运行 setupFiles 文件所花费的时间。
  • Import:导入测试文件及其依赖所花费的时间。这也包括收集所有测试所花费的时间。注意,这不包括测试内部的动态导入。
  • Tests:实际运行测试用例所花费的时间。
  • Environment:设置测试 environment 所花费的时间,例如 JSDOM。

测试运行器

在测试执行时间较高的情况下,你可以生成测试运行器的性能分析文件。参见 NodeJS 文档了解以下选项:

WARNING

--prof 选项由于 node:worker_threads 的限制,无法与 pool: 'threads' 一起使用。

要将这些选项传递给 Vitest 的测试运行器,请在你的 Vitest 配置中定义 execArgv

ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    fileParallelism: false,
    execArgv: [
      '--cpu-prof',
      '--cpu-prof-dir=test-runner-profile',
      '--heap-prof',
      '--heap-prof-dir=test-runner-profile'
    ],
  },
})

测试运行后,应该会生成 test-runner-profile/*.cpuprofiletest-runner-profile/*.heapprofile 文件。参见 检查性能分析记录 了解如何分析这些文件的说明。

参见 性能分析 | 示例 了解示例。

主线程

分析主线程对于调试 Vitest 的 Vite 用法和 globalSetup 文件很有用。 你的 Vite 插件也是在这里运行的。

TIP

参见 性能 | Vite 了解更多关于 Vite 特定性能分析的技巧。

我们推荐使用 vite-plugin-inspect 来分析你的 Vite 插件性能。

为此,你需要将参数传递给运行 Vitest 的 Node 进程。

bash
$ node --cpu-prof --cpu-prof-dir=main-profile ./node_modules/vitest/vitest.mjs --run
#      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                  ^^^^^
#               NodeJS 参数                                           Vitest 参数

测试运行后,应该会生成 main-profile/*.cpuprofile 文件。参见 检查性能分析记录 了解如何分析这些文件的说明。

文件转换

这种性能分析策略是识别由 barrel 文件 引起的不必要转换的好方法。 如果这些日志包含在你的测试运行时不应加载的文件,你可能拥有不必要的导入文件的 barrel 文件。

你也可以使用 Vitest UI 来调试由 barrel 文件引起的缓慢问题。 下面的示例展示了不使用 barrel 文件导入文件如何减少约 85% 的转换文件数量。

├── src
│   └── utils
│       ├── currency.ts
│       ├── formatters.ts  <-- File to test
│       ├── index.ts
│       ├── location.ts
│       ├── math.ts
│       ├── time.ts
│       └── users.ts
├── test
│   └── formatters.test.ts
└── vitest.config.ts
ts
import { expect, test } from 'vitest'
import { formatter } from '../src/utils'
import { formatter } from '../src/utils/formatters'

test('formatter works', () => {
  expect(formatter).not.toThrow()
})
Vitest UI 演示 barrel 文件问题

要查看文件是如何转换的,你可以在 UI 中打开 "Module Info" 视图:

内联模块的模块信息视图内联模块的模块信息视图

文件导入

有些模块加载就是需要很长时间。要识别哪些模块最慢,请在你的配置中启用 experimental.importDurations

vitest.config.ts
ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    experimental: {
      importDurations: {
        print: true,
      },
    },
  },
})

这将在测试完成后打印最慢导入的细分情况:

bash
Import Duration Breakdown (Top 10)

Module                      Self     Total
my-test.test.ts              5ms    620ms [████████████████████]
date-fns/index.js          500ms    500ms [████████████████░░░░] 
src/utils/helpers.ts        10ms    120ms [████████░░░░░░░░░░░░]

你也可以在不更改配置的情况下从 CLI 使用 --experimental.importDurations.print

bash
vitest --experimental.importDurations.print

一旦你识别了慢速模块,有几种策略可以加速导入:

使用特定入口点

许多库提供多个入口点。导入主入口点(通常是 barrel 文件)可能会引入远超你所需的代码。

例如,date-fns 从其主入口点重新导出了数百个函数。不要从顶层模块导入,而是直接从特定函数导入:

ts
import { format } from 'date-fns'
import { format } from 'date-fns/format'

使用 resolve.alias 重定向导入

如果依赖项不提供细粒度的入口点,或者第三方代码导入了沉重的入口点,你可以使用 resolve.alias 将导入重定向到更轻量级的替代方案:

vitest.config.ts
ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  resolve: {
    alias: [
      {
        find: /^date-fns$/,
        replacement: join(dirname(require.resolve('date-fns/package.json')), 'index.cjs'),
      },
    ]
  },
})

使用依赖优化器

Vitest 可以使用 deps.optimizer 将外部库捆绑到单个文件中,这减少了导入具有许多内部模块的包的开销:

vitest.config.ts
ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    deps: {
      optimizer: {
        ssr: {
          enabled: true,
          include: ['date-fns'],
        },
      },
    },
  },
})

这对于 UI 库和具有深层导入树的包特别有效。对 node/edge 环境使用 optimizer.ssr,对 jsdom/happy-dom 环境使用 optimizer.client

代码覆盖率

如果你的项目中代码覆盖率生成缓慢,你可以使用 DEBUG=vitest:coverage 环境变量来启用性能日志记录。

bash
$ DEBUG=vitest:coverage vitest --run --coverage

 RUN  v3.1.1 /x/vitest-example

  vitest:coverage Reading coverage results 2/2
  vitest:coverage Converting 1/2
  vitest:coverage 4 ms /x/src/multiply.ts
  vitest:coverage Converting 2/2
  vitest:coverage 552 ms /x/src/add.ts
  vitest:coverage Uncovered files 1/2
  vitest:coverage File "/x/src/large-file.ts" is taking longer than 3s
  vitest:coverage 3027 ms /x/src/large-file.ts
  vitest:coverage Uncovered files 2/2
  vitest:coverage 4 ms /x/src/untested-file.ts
  vitest:coverage Generate coverage total time 3521 ms

这种性能分析方法非常适合检测被覆盖率提供者意外选取的大文件。 例如,如果你的配置意外地将大型构建后的压缩 Javascript 文件包含在代码覆盖率中,它们应该出现在日志中。 在这些情况下,你可能想要调整你的 coverage.includecoverage.exclude 选项。

检查性能分析记录

你可以使用各种工具检查 *.cpuprofile*.heapprofile 的内容。参见下面的列表示例。