[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

refactor(benchmark): simplify provider contract to a default export

Address review feedback on the pluggable benchmark provider API. The
initial design mirrored the coverage provider (a `getProvider` factory
plus a `BenchmarkProviderModule` wrapper), which was a special case that
benchmarks — running only in tests — don't need.

- Drop `BenchmarkProviderModule` and `getProvider`: a custom provider
module now default-exports a `BenchmarkProvider` directly.
- Reuse the existing `TestModuleRunner` interface instead of the bespoke
`BenchmarkProviderLoader`.
- Remove the `TestRunner.runBenchmarks` override hook, now redundant with
the provider API, and inline `tinybench.run()` in the default provider.

Guillaume Lagrange (Jul 22, 2026, 10:37 AM +0200) bbcdb4d9 3dbe73a9

+32 -64
+1 -1
packages/vitest/src/node/types/benchmark.ts
··· 32 32 * The benchmark provider that executes registered benchmarks and produces 33 33 * their results. Either the built-in `'default'` provider (backed by 34 34 * tinybench) or a path to a module whose default export implements 35 - * `BenchmarkProviderModule`. The path is resolved relative to the project 35 + * `BenchmarkProvider`. The path is resolved relative to the project 36 36 * root. 37 37 * 38 38 * @default 'default'
-1
packages/vitest/src/public/index.ts
··· 44 44 BenchHooks, 45 45 BenchmarkGroup, 46 46 BenchmarkProvider, 47 - BenchmarkProviderModule, 48 47 BenchRegistration, 49 48 BenchRegistrationInput, 50 49 BenchResult,
+10 -22
packages/vitest/src/runtime/benchmark.ts
··· 7 7 TaskResultTimestampProviderInfo, 8 8 } from 'tinybench' 9 9 import type { SerializedConfig } from './config' 10 + import type { TestModuleRunner } from './moduleRunner/testModuleRunner' 10 11 import type { BaselineData, Test, TestBenchmark, TestBenchmarkTask } from './runner/types' 11 12 import { isAbsolute, relative } from 'pathe' 12 13 import c from 'tinyrainbow' ··· 81 82 /** 82 83 * Executes the benchmarks of a single test and returns their results. 83 84 * 85 + * A custom provider module must default-export an object of this shape. 86 + * 84 87 * The returned array is the sole source of results: it's what `bench().run()` 85 88 * resolves to and what the reporter serializes. Results are matched to 86 89 * registrations by `name`; return one {@link BenchResult} per registration. ··· 91 94 run: (group: BenchmarkGroup) => Promise<BenchResult[]> 92 95 } 93 96 94 - /** 95 - * The module contract a custom benchmark provider must satisfy. The module's 96 - * default export must be an object of this shape. 97 - * 98 - * @experimental 99 - */ 100 - export interface BenchmarkProviderModule { 101 - /** Factory that creates the provider used to run benchmarks. */ 102 - getProvider: () => BenchmarkProvider | Promise<BenchmarkProvider> 103 - } 104 - 105 - interface BenchmarkProviderLoader { 106 - import: (id: string) => Promise<Record<string, any>> 107 - } 108 - 109 97 let cachedProvider: Promise<BenchmarkProvider> | undefined 110 98 111 99 async function loadProviderModule( 112 100 provider: string, 113 - loader: BenchmarkProviderLoader, 114 - ): Promise<BenchmarkProviderModule> { 101 + moduleRunner: TestModuleRunner, 102 + ): Promise<BenchmarkProvider> { 115 103 let mod: Record<string, any> 116 104 try { 117 - mod = await loader.import(provider) 105 + mod = await moduleRunner.import(provider) 118 106 } 119 107 catch (error) { 120 108 throw new Error( ··· 137 125 */ 138 126 export function resolveBenchmarkProvider( 139 127 config: SerializedConfig, 140 - loader: BenchmarkProviderLoader, 128 + moduleRunner: TestModuleRunner, 141 129 ): Promise<BenchmarkProvider> { 142 130 if (!cachedProvider) { 143 131 const provider = config.benchmark.provider 144 132 cachedProvider = provider === 'default' || !provider 145 133 ? Promise.resolve(createDefaultBenchmarkProvider(config)) 146 - : Promise.resolve(loadProviderModule(provider, loader)).then(mod => mod.getProvider()) 134 + : loadProviderModule(provider, moduleRunner) 147 135 } 148 136 return cachedProvider 149 137 } ··· 240 228 export function createBench( 241 229 test: Test, 242 230 config: SerializedConfig, 243 - loader: BenchmarkProviderLoader, 231 + moduleRunner: TestModuleRunner, 244 232 ): Bench { 245 233 const pending = new Set<BenchRegistration<any>>() 246 234 ··· 352 340 const getterTracker = workerState.getterTracker 353 341 getterTracker?.resetInvocations() 354 342 try { 355 - const provider = await resolveBenchmarkProvider(config, loader) 343 + const provider = await resolveBenchmarkProvider(config, moduleRunner) 356 344 const results = await provider.run({ test, config: config.benchmark, registrations, options }) 357 345 const byName = new Map<string, BenchResult>() 358 346 for (const result of results) {
+1 -2
packages/vitest/src/runtime/benchmark/default-provider.ts
··· 2 2 import type { BenchmarkGroup, BenchmarkProvider, BenchResult } from '../benchmark' 3 3 import type { SerializedConfig } from '../config' 4 4 import { Bench as Tinybench } from 'tinybench' 5 - import { TestRunner } from '../runners/test' 6 5 7 6 const now = globalThis.performance 8 7 ? globalThis.performance.now.bind(globalThis.performance) ··· 27 26 for (const { name, fn, fnOpts } of registrations) { 28 27 tinybench.add(name, fn, fnOpts) 29 28 } 30 - await TestRunner.runBenchmarks(tinybench) 29 + await tinybench.run() 31 30 32 31 const errors = tinybench.tasks 33 32 .filter(task => task.result.state === 'errored')
-10
packages/vitest/src/runtime/runners/test.ts
··· 1 1 import type { SpanOptions } from '@opentelemetry/api' 2 2 import type { ExpectStatic } from '@vitest/expect' 3 - import type { Bench as Tinybench, Task as TinybenchTask } from 'tinybench' 4 3 import type { ModuleRunner } from 'vite/module-runner' 5 4 import type { Traces } from '../../utils/traces' 6 5 import type { Bench } from '../benchmark' ··· 301 300 static setTestFn: typeof getFn = getFn 302 301 static matchesTags: typeof matchesTags = matchesTags 303 302 static createFileTask: typeof createFileTask = createFileTask 304 - 305 - /** 306 - * @experimental 307 - * A function that runs tinybench tasks. 308 - * Can be overriden to run tasks in a special environment. 309 - */ 310 - static async runBenchmarks(tinybench: Tinybench): Promise<TinybenchTask[]> { 311 - return await tinybench.run() 312 - } 313 303 } 314 304 315 305 function clearModuleMocks(config: SerializedConfig) {
+20 -28
test/e2e/test/benchmarking.test.ts
··· 1339 1339 } 1340 1340 } 1341 1341 export default { 1342 - getProvider() { 1343 - return { 1344 - async run({ registrations }) { 1345 - return registrations.map((reg, i) => ({ 1346 - name: reg.name, 1347 - state: 'completed', 1348 - latency: stats((i + 1) * 100), 1349 - throughput: stats(1 / ((i + 1) * 100)), 1350 - period: (i + 1) * 100, 1351 - totalTime: (i + 1) * 1000, 1352 - })) 1353 - }, 1354 - } 1342 + async run({ registrations }) { 1343 + return registrations.map((reg, i) => ({ 1344 + name: reg.name, 1345 + state: 'completed', 1346 + latency: stats((i + 1) * 100), 1347 + throughput: stats(1 / ((i + 1) * 100)), 1348 + period: (i + 1) * 100, 1349 + totalTime: (i + 1) * 1000, 1350 + })) 1355 1351 }, 1356 1352 } 1357 1353 `, ··· 1399 1395 } 1400 1396 } 1401 1397 export default { 1402 - getProvider() { 1403 - return { 1404 - async run({ registrations }) { 1405 - const out = [] 1406 - for (const reg of registrations) { 1407 - await reg.fnOpts?.beforeAll?.() 1408 - await reg.fn() 1409 - await reg.fnOpts?.afterAll?.() 1410 - out.push({ 1411 - name: reg.name, state: 'completed', 1412 - latency: stats(1), throughput: stats(1), period: 1, totalTime: 1, 1413 - }) 1414 - } 1415 - return out 1416 - }, 1398 + async run({ registrations }) { 1399 + const out = [] 1400 + for (const reg of registrations) { 1401 + await reg.fnOpts?.beforeAll?.() 1402 + await reg.fn() 1403 + await reg.fnOpts?.afterAll?.() 1404 + out.push({ 1405 + name: reg.name, state: 'completed', 1406 + latency: stats(1), throughput: stats(1), period: 1, totalTime: 1, 1407 + }) 1417 1408 } 1409 + return out 1418 1410 }, 1419 1411 } 1420 1412 `,