CI integrations for JUnit XML test reports
TestNod accepts JUnit XML from any CI provider, so each page in this section is a copy-paste recipe paired with a short note on what it does. Every recipe is the same upload contract documented in the Submission API, wrapped in provider-specific syntax.
Pick your CI provider
| Provider | Page |
|---|---|
| GitHub Actions | GitHub Actions |
| Anything else | Generic CI / curl |
Integrations with more CI providers, including GitLab CI, CircleCI, Jenkins, and Buildkite, will be added soon.
The steps every upload recipe follows
Whichever provider you start from, every recipe walks through the same five steps:
- Run tests, write JUnit XML. Configure your test framework to produce a JUnit-formatted report at a known path.
- Set the project token from a CI secret. Each provider has its own UI for storing secrets, and the recipes show you where to put it.
- Upload the report with a
build_id, even if tests failed. Pass the CI's build identifier astest_run[metadata][build_id]and use the provider's "always run" directive so the upload happens whether the tests pass or fail. Skipping the upload when tests fail is the most common mistake teams make on day one, and a failed run is exactly the one you want recorded. - Pass branch, commit, run URL, and any tags. These are all optional, but all worth setting. See Run metadata for what each one enables.
- Finalize the build. Once every upload for the build has been sent, call
POST /integrations/test_runs/finalizewith the samebuild_id. The run stays inpendinguntil finalize lands, so this is the step that completes it.
Parallel and matrix builds
Most CI providers can run the same job across a matrix of language versions, operating systems, or runners. Every shard uploads with the same build_id so they all attach to a single run, and one follow-up job calls /finalize after the matrix finishes. The GitHub Actions page spells out the needs: syntax for ordering that finalize job after the matrix.
Tag each shard with its matrix axis so the dashboard and alert engine can compare like with like:
-F "tags[][value]=ruby-$RUBY_VERSION"