Testris
A smarter tool for splitting Go tests across multiple CI parallel jobs.
Features
- Duration-based splitting: Balance test execution time across nodes, not just test count
- Timing cache: Learn from previous runs to optimize future splits
- Package grouping: Keep tests from the same package together
- JSON output: Structured output for easy integration with other tools
- Benchmark & fuzz support: Split more than just unit tests
- Config file: Set project defaults in
.testris.yaml
Installation
go install github.com/mcncl/testris@latest
Usage
Basic splitting
# Get JSON output with test pattern and metadata
testris split -i 0 -t 2
# Extract just the pattern for go test
go test -run "$(testris split -i 0 -t 2 | jq -r '.pattern')" ./...
With timing data (smarter splitting)
First, capture timing data from a test run:
go test -json ./... | testris import
Then split using the timing data:
testris split -i 0 -t 2 -T .testris-timing.json | jq -r '.pattern'
Example output
{
"index": 0,
"total": 2,
"test_count": 10,
"pattern": "^(TestOne|TestTwo|TestThree)$",
"estimated_duration": "1.5s",
"tests": [
{"name": "TestOne", "package": "pkg", "type": "test"},
{"name": "TestTwo", "package": "pkg", "type": "test"}
],
"packages": [
{"name": "pkg", "count": 2, "duration": "1.5s"}
]
}
In Buildkite
steps:
- label: "go test"
command: |
# Split tests (uses timing cache if available)
if [ -f .testris-timing.json ]; then
TEST_PATTERN=$(testris split -i $$BUILDKITE_PARALLEL_JOB -t $$BUILDKITE_PARALLEL_JOB_COUNT -T .testris-timing.json | jq -r '.pattern')
else
TEST_PATTERN=$(testris split -i $$BUILDKITE_PARALLEL_JOB -t $$BUILDKITE_PARALLEL_JOB_COUNT | jq -r '.pattern')
fi
# Run tests with JSON output for timing capture
go test ./... -run "$$TEST_PATTERN" -v -json | tee test-output.json
# Import timing data for future runs
testris import -T .testris-timing.json < test-output.json
parallelism: 2
artifact_paths:
- ".testris-timing.json"
Commands
testris split
Split tests across parallel jobs.
| Flag |
Short |
Default |
Description |
--index |
-i |
0 |
Current parallel index (0-based) |
--total |
-t |
1 |
Total number of parallel jobs |
--dir |
-d |
. |
Root directory to scan for tests |
--timing |
-T |
|
Path to timing cache file |
--group-by |
-g |
none |
Group tests: none or package |
--include |
-I |
tests |
Test types: tests, benchmarks, fuzz |
--format |
-f |
json |
Output format: json or stats |
--config |
-c |
|
Path to config file (default: .testris.yaml) |
testris import
Import timing data from go test -json output.
| Flag |
Short |
Default |
Description |
--timing |
-T |
.testris-timing.json |
Path to timing cache file |
Reads from stdin:
go test -json ./... | testris import
Configuration
Create a .testris.yaml file in your project root to set defaults:
# .testris.yaml
timing: .testris-timing.json
group_by: package
include:
- tests
- benchmarks
format: json
CLI flags override config file values.