Test and bench runners
Understand runner selection, project configuration, and results from test and benchmark commands.
lpm test and lpm bench choose a runner from the package manifest. Each selected workspace member can use a different runner. The dedicated commands provide a common interface for local use and CI.
lpm test
lpm bench
lpm test --filter web -- --coverageQuickstart
Install the runner declared by the project before its first use:
lpm install
lpm test -- --coverage
lpm bench --jsonVitest runs once by default. A package script controls its own runner behavior. For prerequisite tasks or cached output, use lpm run.
Runner selection
LPM CLI reads dependencies and devDependencies in package.json.
Declaration order does not change runner priority.
Each member needs its own runner declaration. A dependency declared only at the workspace root does not select that runner for a member.
A declared runner takes priority over the package script. Detection does not install the runner. If its executable is missing, run lpm install. LPM CLI searches local executables within the current project or workspace.
For an explicit package script, use lpm run:
lpm run test
lpm run benchUse package scripts for other runners, such as node --test or bun test.
A declared Vitest, Jest, or Mocha dependency can still take priority for the dedicated test command.
Project configuration
The commands have no dedicated test or bench section in lpm.json.
They use project environment configuration and managed runtime pins.
{
"runtime": { "node": "22" },
"env": { "test": ".env.test", "bench": ".env.bench" }
}For package-script fallback, tasks.<name>.env takes priority over env.<name>.
If tasks.<name>.command exists, the package script remains the command.
Pre-hooks and post-hooks run in sequence with npm lifecycle variables.
A failed phase stops later phases.
Extra arguments apply to the main script.
Direct runners use the default project environment. Both paths apply environment schemas and managed runtimes. The dedicated commands do not use task caching or task dependencies. See lpm run for those workflows.
Workspace members inherit root runtime pins and can override each runtime locally. Engine checks use the Node executable on the execution PATH. See managed runtimes for automatic downloads and fallback behavior.
Arguments and watch mode
Put runner arguments after --:
lpm test -- --coverage
lpm test -- --all
lpm test --filter web -- --watch
lpm bench --filter web -- --watch
lpm test --filter web -- -uw
lpm test --all -- -- --watchThe first -- separates LPM CLI flags from runner arguments.
A second -- belongs to the runner. Tokens after it do not select watch mode.
Vitest short-option bundles such as -uw also request watch mode.
Vitest tests use vitest run. Benchmarks use vitest bench --run.
Both commands run once by default, including in an interactive terminal.
For an explicit watch request, LPM CLI removes the default run or --run argument.
Use --watch=false to disable Vitest watch mode.
Vitest treats --watch=off and --watch=0 as enabled.
Jest's -w <N> controls worker count.
Its --watch and --watchAll flags request watch mode.
Mocha receives its own watch arguments directly.
A package script controls its own runner behavior.
A workspace watch request can select exactly one member. LPM CLI rejects watch requests that select multiple members. For an empty selection, an explicit long watch flag reports that there is nothing to watch.
Workspace execution
lpm test --all --workspace-concurrency 2 --json
lpm bench --filter './packages/*' --fail-if-no-match --jsonWorkspace members start when their selected dependencies finish. The scheduler fills available slots as members finish. It does not wait for unrelated members at the same dependency depth. The concurrency limit bounds active members. Failed members produce a nonzero result after the selected work completes. Task prerequisites and task caching do not apply to these dedicated commands.
Results and cleanup
Human mode streams child output to the terminal. JSON mode closes stdin and emits one result after completion:
lpm --json test
lpm --json bench --allThe result contains member status, exit codes, and elapsed time. Failed members include captured output or a launch error. Successful members omit captured output. Each captured stream has a 10 MiB limit.
Single-package runs preserve the runner or hook exit code.
If any selected member fails in a finite workspace run, the command exits with 1.
Workspace member results retain the individual exit codes.
A watched selection of one member uses single-package execution and preserves the runner exit code.
Preparation failures use the global error object or a member error with exit_code: null.
On Unix, stop signals trigger child cleanup before LPM CLI exits.
A daemon that detaches and outlives its parent manages its own shutdown.
SIGINT produces status 130, and SIGTERM produces 143.
In JSON mode, bounded output remains available in the failure result.
Common recovery steps
| Problem | Action |
|---|---|
| The wrong runner starts. | Check each member's dependency declarations or use an explicit package script through lpm run. |
| A detected executable is absent. | Run lpm install in the project. |
| A watch request selects multiple members. | Narrow the selection to one member. |
| Test-specific environment values are absent. | Use package-script fallback with tasks.test.env, or run the script explicitly through lpm run. |
| A command needs prerequisites or caching. | Configure lpm.json tasks and use lpm run. |
See the command pages for complete workspace selectors and flags.
See also
- lpm test — test examples and flags
- lpm bench — benchmark examples and flags
- lpm run — package scripts and configured tasks
- Workspaces — filters and dependency selection