1. Get JUnit XML from two or more runs
Any framework that writes JUnit XML works:
pytest --junitxml=run1.xml # then run it again for run2.xml
jest --reporters=jest-junit # needs jest-junit installed
go test ./... 2>&1 | go-junit-report > run.xml
mvn test # Surefire writes target/surefire-reports/*.xml automatically
If your CI already reruns failed tests, that rerun report is exactly what you want here — it does not need to contain every test, only the ones it ran.
2. Paste each run's XML
Paste at least two runs and click "Find flaky tests". One run only works for Surefire's own <flakyFailure> elements — everything else needs at least two.
Verdict
Show stable tests too
Prefer a CLI, or want this as a CI gate?
Same logic, zero dependencies, one file:
python3 check.py run1.xml run2.xml run3.xml # exits 1 if any test is flaky
python3 check.py --glob "reports/*.xml" # or glob a directory of reports
python3 check.py run1.xml run2.xml --json # machine-readable
Get check.py from the repo — stdlib only, nothing to install. It reads local files and makes no network call.
Common questions
How is 'flaky' decided?
A test that failed in at least one run and passed in at least one other run of the same nominal suite is flaky. That is a fact about the reports you gave it, not a guess or a model output — no AI, no heuristics beyond counting pass/fail occurrences per test.
What if my rerun only re-ran the tests that failed?
That is the most common real shape and it is handled: a test does not have to appear in every report to be classified. If it failed in the full run and passed in the rerun-of-failures, it is still counted as flaky using exactly the occurrences it has.
Which test frameworks does this work with?
Anything that emits JUnit XML: pytest (--junitxml), Jest via jest-junit, Go via go-junit-report, PHPUnit, RSpec via RSpecJUnitFormatter, and Maven/Gradle Surefire/Failsafe. Surefire's own <flakyFailure>/<flakyError> elements are also read directly. Even a single Surefire report with retries enabled can surface flakiness this way.
Is my test output uploaded anywhere?
No. The browser page parses the XML you paste entirely in your own browser and never sends it anywhere. The CLI reads local files and makes no network call at all.
More: why tests go flaky in CI and what to do about each one, how this compares to BuildPulse, a paid, connected, always-on alternative, or why eyeballing two CI logs gets this wrong once a suite reruns only its failures.
Related: if every check fails in a few seconds with an empty body, first check whether GitHub assigned a runner with the actions runner starvation check — paste the run’s job JSON and it reads runner_id.
Related: once the suite is stable, split it across CI shards by measured duration with the CI test shard balancer — it reads the same JUnit XML and prints the files for each shard.
Related: if tests are stable but line coverage fell on the PR, the coverage drop check compares two Cobertura or JaCoCo reports and names the files that got worse — nothing uploaded.