Skip to content

JUnit (Java) test reporting

JUnit with Maven Surefire (or Gradle) writes a folder of XML reports — but those are files on a CI runner, not a dashboard, and they’re gone next build. Qualflare ingests your JUnit XML and turns it into hosted, historical reporting: AI clusters failures by root cause, reads flaky status straight from the XML, and rates each launch’s risk — across every CI run and every module.

Why the JUnit XML reports aren’t enough

Maven Surefire and Gradle both produce solid per-build artifacts: a folder of JUnit *.xml files, plus a local HTML report (target/site or build/reports/tests/). All of it is per-build and lives on the runner. Nothing collects results across builds, aggregates a multi-module reactor into one view, or tells you whether a test has been getting flakier over the last two weeks. For that you need something that stores results over time and analyzes them.

Send JUnit results to Qualflare

Two steps. First, run your tests — Maven Surefire and Gradle write JUnit XML automatically, no extra plugin:

# Maven (Surefire) and Gradle both write JUnit XML automatically
mvn test      # → target/surefire-reports/*.xml
# or
gradle test   # → build/test-results/test/*.xml

Then upload those files with the Qualflare CLI. The --format junit flag uses the JUnit-XML parser; the CLI attaches your Git branch and commit automatically:

# Upload the JUnit XML to Qualflare
qf my-project collect "target/surefire-reports/*.xml" --format junit

In CI, that’s one extra step after your build (GitHub Actions shown — GitLab CI, Bitbucket Pipelines, and Jenkins work the same way). Authenticate the CLI once with your Qualflare access token, stored as a CI secret — see the CLI docs.

# .github/workflows/tests.yml
- name: Run tests
  run: mvn test

- name: Upload results to Qualflare
  if: always() # upload even when tests fail — that's the point
  run: qf my-project collect "target/surefire-reports/*.xml" --format junit

What you get on top of JUnit XML

  • AI failure clustering. When a single broken dependency or config takes down 30 tests, Qualflare groups them by root cause — so you fix one thing instead of opening 30 stack traces.
  • Flaky scoring from your JUnit XML. Qualflare reads retry counts and flaky status directly from JUnit-XML results — no extra configuration — and also scores flakiness from history across runs. (The same applies to TestNG and other tools that emit JUnit XML.)
  • Multi-module aggregation. Upload every module’s XML and Qualflare merges them into one launch — a single picture across the whole reactor instead of a report per module.
  • Per-launch risk. Each CI run becomes a launch with a risk rating, the failing areas, and recommended next steps — a ship / don’t-ship signal that arrives with the results.
  • History, trends & defects. Pass rate, slowest tests, and flakiness over time across branches — plus a defect you can open straight from a failing run.

Raw JUnit reports vs Qualflare

  Surefire / Gradle reports Qualflare
History across CI buildsYes
Aggregates multi-module buildsYes
AI failure clustering (root cause)Yes
Flaky scoring over timeYes
Local, zero-setup, offlineYes

Complementary: keep the Surefire / Gradle reports for local builds, add Qualflare for hosted, historical CI observability.

Get AI analysis on your JUnit runs

Start free — run mvn test, point qf collect at the XML, and get your first AI analysis in minutes.

Get Started Free

Qualflare works the same with Playwright, pytest, Cypress, Jest and 20+ more frameworks. Weighing tools? See how it compares to other test management platforms, or browse all framework reporting guides.

One schema, many dialects

“JUnit XML” is less a standard than a family of dialects. Maven Surefire writes one file per test class under target/surefire-reports/; Gradle does the same under build/test-results/; tools like pytest and PHPUnit emit a single file wrapped in <testsuites>. Attribute conventions differ too — what counts as an error vs a failure, where skip reasons live, whether timestamps are present:

<!-- The same schema, three dialects -->
<!-- Maven Surefire: target/surefire-reports/TEST-*.xml (one file per class) -->
<!-- Gradle:         build/test-results/test/TEST-*.xml                      -->
<!-- pytest/PHPUnit: a single results.xml with a <testsuites> wrapper        -->
<testsuite name="com.acme.CheckoutTest" tests="12" failures="1" errors="0" skipped="2">
  <testcase classname="com.acme.CheckoutTest" name="appliesDiscount" time="0.42"/>
</testsuite>

The practical consequence: a Java multi-module build produces a directory of XML files, not one file. Any reporting pipeline has to sweep those files and treat the set as one run — which is why the CLI accepts glob patterns and merges every matched file into a single launch:

# Multi-module build: glob every module's XML into one collect
qf my-project collect "target/surefire-reports/TEST-*.xml" --format junit

JUnit XML is also the least detailed of the formats on this site — no retry counts, no attachments, coarse failure text. It’s the right choice when it’s what your build already produces (Java, .NET, PHP, Ruby); if your runner can also emit a richer native JSON (Playwright, Jest, Cypress), prefer that and keep JUnit for the CI platform’s own test tab.

Frequently asked questions

How do I send JUnit results to Qualflare?

Maven Surefire and Gradle write JUnit XML automatically when you run your tests — to target/surefire-reports/ and build/test-results/test/ respectively. Upload those files with the Qualflare CLI: qf <project> collect "target/surefire-reports/*.xml" --format junit. The CLI attaches your Git branch and commit and turns the run into a tracked launch with AI analysis.

Does Qualflare detect flaky JUnit tests?

Yes. Qualflare reads retry counts and flaky status straight from JUnit XML — no extra flags — so when your XML already marks reruns (for example Maven Surefire’s rerunFailingTestsCount, or @RepeatedTest), that data flows through. It also scores each test’s flakiness from its pass/fail history across runs, so you get flaky signal even without in-run reruns.

Maven or Gradle — does it matter?

Neither needs special setup. Maven Surefire writes target/surefire-reports/*.xml on mvn test; Gradle writes build/test-results/test/*.xml on gradle test. Point qf collect at whichever path your build uses with --format junit. For a multi-module build, upload all modules’ XML and Qualflare aggregates them into one launch.

Does it work with TestNG?

Yes. TestNG emits JUnit-compatible XML, so the same flow works — use --format junit, or --format testng for the TestNG-aware parser. Like JUnit, retry and flaky status present in the XML are captured automatically.

Does it work in GitHub Actions, GitLab CI, and Jenkins?

Yes. Add a step after your mvn test / gradle test that calls qf <project> collect on the XML. The CLI auto-attaches the Git branch and commit (or pass --branch/--commit), so each CI run becomes a tracked launch. The same flow works across GitHub Actions, GitLab CI, Bitbucket Pipelines, and Jenkins.

Setup reflects the Qualflare CLI (docs.qualflare.com) and the JUnit XML that Maven Surefire and Gradle produce as of June 2026. Written by İbrahim Süren, Qualflare.