Runners
Test parallelization Private beta
One workflow step runs your test suite as several parts at the same time. The results come back to the original job: one log, one list of failures, one pass or fail.
Add it to your workflow
Replace your test step with the action and put your test command in run:
- uses: vitko-inc/split-tests@v1
with:
run: pytest -q tests/| Tool | Example run | Shared out by |
|---|---|---|
| pytest | pytest -q tests/ | test |
| cargo nextest | cargo nextest run --workspace --no-fail-fast | test |
| Go | go test ./... | package |
| Jest | npx jest --ci | file |
| Vitest | npx vitest run | file |
Run the test tool itself in run (for example uv run pytest, not tox or make test), as a single command. A shell script with &&, pipes or several lines runs in one part, and the log says so.
Any other tool: set tool: command. Your command runs in every part with VITKO_PART (1, 2, …) and VITKO_PARTS, and chooses its own share.
- uses: vitko-inc/split-tests@v1
with:
tool: command
run: ./scripts/run-tests.sh --part "$VITKO_PART" --of "$VITKO_PARTS"Install and build first
The parts start when your tests start and can’t reach the network. Install and build everything before the split: in an earlier step, or in prepare, which runs first in this job, with network.
- uses: vitko-inc/split-tests@v1
with:
prepare: uv sync --locked
run: uv run --locked pytest -qA command that installs as it starts (such as tox) does that in every part, and fails there.
Tests that need the network
Mark them, leave them out of the split, and run them in a separate step:
- uses: vitko-inc/split-tests@v1
with:
run: pytest -q -m "not network"
- run: pytest -q -m networkWith Go, use -skip in the split and -run in the separate step. With Jest or Vitest, use a separate config or a file-name pattern.
How many parts
parts: auto (the default) picks a number that suits your plan and the size of your suite. A suite estimated at under a minute runs in one part, because splitting it would cost more than it saves; the log says so. Set parts to a number to split anyway. After the first run, Vitko remembers how long each test took and balances the parts so they finish close together.
Inputs and outputs
| Input | Default | What it does |
|---|---|---|
run | required | Your test command. |
prepare | A command to run first, in this job, with network. | |
parts | auto | How many parts. |
tool | auto | pytest, nextest, jest, vitest, go or command. |
env | Extra environment variables your tests need, by name. | |
junit | vitko-split-tests.xml | JUnit report path; empty to skip. |
working-directory | . | Where to run the tests. |
timings-file | Optional test durations, if you keep them. |
Outputs: junit, parts and failed.
The environment your tests see
The parts get the usual build and toolchain variables (PATH, HOME, CI, CARGO_*, PYTHONPATH, NODE_OPTIONS, GO* and similar), plus the names you list in env. Names that look like secrets (containing TOKEN, SECRET, PASSWORD, PRIVATE, CREDENTIAL, or ending in _KEY) are never passed. Start services such as a database before this step; each part gets its own copy, already running.
Results
- Each part’s output in a section you can expand, and every failed test in one place.
- Failed tests as annotations (up to 10), a table of parts in the run summary, and a JUnit report.
- The step fails if any test fails, a part stops early, or a test doesn’t report a result. It never passes on partial results.
Limits
- Up to your plan’s limit of parts at once.
- One split-tests step at a time in a job, and up to four in one job.
- Tests that depend on running in a fixed order, or on each other, can fail when split, as with any parallel test runner.
Billing
You pay for the time the parts run. The original job’s wait while its parts run isn’t charged. Short suites gain little, which is why auto doesn’t split them.
run: step. Keep it in workflows that run in both places. Use @v1, or pin a release such as @v1.0.4.