Help Center/Automation & CI/CD/Your test framework

JUnit XML Format Explained: Example File, Schema, and How Tools Read It

Learn the JUnit XML format with a valid example, common elements, framework commands, TestCollab ID mapping, attachments, configurations, and uploads.

On this page

JUnit XML is a de-facto XML format for exchanging automated test results. It began in the Java testing ecosystem, but CI services and test frameworks across many languages now generate or consume it because the format can represent suites, individual cases, durations, failures, errors, skipped tests, and captured output in one portable file.

TestCollab reads JUnit XML through tc report --format junit. For the standard three-step reporting workflow, start with Automated Testing in a CI/CD Pipeline with TestCollab.

The standard reporting workflow is: create a Test Plan with tc createTestPlan, run tests that generate JUnit XML, then upload the file with tc report. Recording a build and adding a quality gate are optional extensions, not requirements for uploading results.

A JUnit XML Example File

Save this as results.xml. It contains one passed case, one failed case with an attachment marker, and one skipped case.

<?xml version="1.0" encoding="UTF-8"?>
<testsuites tests="3" failures="1" errors="0" skipped="1" time="0.62">
  <testsuite name="Authentication" tests="3" failures="1" skipped="1" time="0.62">
    <testcase
      classname="Authentication.Login"
      name="[TC-42] Login succeeds with valid credentials"
      time="0.12" />

    <testcase
      classname="Authentication.Login"
      name="[TC-43] Invalid password is rejected"
      time="0.43">
      <failure message="Expected HTTP 401 but received 200" type="AssertionError">
        at authentication.spec.ts:27
      </failure>
      <system-out><![CDATA[
[[ATTACHMENT|screenshots/invalid-password.png]]
      ]]></system-out>
    </testcase>

    <testcase
      classname="Authentication.SSO"
      name="[TC-44] SSO login redirects to the identity provider"
      time="0.07">
      <skipped message="Identity provider unavailable in this environment" />
    </testcase>
  </testsuite>
</testsuites>

The file must be well-formed XML. Attribute order and indentation do not matter, and many reporters omit aggregate counters or the outer <testsuites> wrapper.

The JUnit XML Elements That Matter

Element or attribute

Purpose

<testsuites>

Optional outer container for one or more suites. Some tools write a single <testsuite> as the document root instead.

<testsuite>

Groups related cases. Suites may be flat or nested. The name is commonly a class, file, package, feature, or component.

<testcase>

Represents one test result. Common attributes are name, classname, and time.

<failure>

Marks an assertion failure. The message or type attribute summarizes it, and the body usually contains a stack trace.

<error>

Usually means the test could not complete because setup, teardown, or the runner failed. Most consumers treat it as a failed case.

<skipped>

Marks a case that did not run. It may contain a reason in message.

<system-out>

Captured standard output. TestCollab also reads whole-line attachment markers from it.

time

Duration in seconds in most JUnit dialects. It may appear on cases, suites, and the outer container.

tests, failures, errors, skipped

Aggregate counters written by many reporters. Consumers may recalculate totals from the actual <testcase> elements.

How a Test Case Status Is Represented

  • Passed: a <testcase> with no <failure>, <error>, or <skipped> child

  • Failed: a case containing <failure> or <error>

  • Skipped: a case containing <skipped>, or a reporter-specific skipped status attribute

A self-closing <testcase ... /> is normally a passed test, not a missing result.

Is There an Official JUnit XML Schema?

There is no single, universally authoritative JUnit XML schema that every producer follows. The name describes a family of compatible, de-facto formats produced by JUnit build integrations and adopted by other test tools.

Common differences include whether <testsuites> is present, whether suites are nested, which aggregate attributes are required, how properties and timestamps are represented, and whether a runner uses <failure> or <error>. For interoperability, rely on the common elements above and generate the file with your framework's maintained reporter instead of constructing XML by hand.

How to Generate JUnit XML

Framework

Command or output

Playwright

PLAYWRIGHT_JUNIT_OUTPUT_NAME=results.xml npx playwright test --reporter=junit

pytest

pytest --junitxml=results.xml

Jest

Install jest-junit, then run JEST_JUNIT_OUTPUT_DIR=./reports npx jest --reporters=default --reporters=jest-junit.

Mocha

Install mocha-junit-reporter, then run MOCHA_FILE=results.xml npx mocha --reporter mocha-junit-reporter.

JUnit 4 or 5 with Maven Surefire

mvn test, with reports in target/surefire-reports/.

TestNG

mvn test, with JUnit-compatible reports in test-output/junitreports/ when using TestNG's standard output.

PHPUnit

phpunit --log-junit results.xml

Robot Framework

robot --xunit results.xml tests/, or convert an existing Robot output with rebot --xunit results.xml output.xml.

How TestCollab Reads JUnit XML

tc report reads each <testcase> in document order. It uses nested <testsuite> names for hierarchy when the document contains nested suites; for a flat report, it uses classname as the suite name when available.

Test Case ID Mapping

For an existing TestCollab plan, place the TestCollab ID in the name or classname. The recommended form is:

<testcase name="[TC-42] Login should succeed" classname="Authentication" />

Supported explicit patterns are [TC-42], TC-42, id-42, and testcase-42, matched without case sensitivity. Explicit markers take priority over every fallback.

A marker-free title is treated as an ID only when the entire title is a slug ending in a hyphen and digits, such as checkout-42 or login-flow-42. Ordinary prose or technical names such as Returns HTTP 200, UTF-8, and SHA-256 are not interpreted as IDs. Use [TC-42] to make the mapping unambiguous.

Screenshots, Logs, and Traces

Put an attachment marker on its own line inside the case's <system-out>:

<system-out><![CDATA[
test finished
[[ATTACHMENT|test-results/login-failure.png]]
[[ATTACHMENT|test-results/browser.log]]
]]></system-out>

Paths may be absolute, relative to the directory where tc report runs, or relative to the result file. TestCollab uploads up to 10 files per case, with a maximum of 10 MB per file. A missing or oversized file produces a warning but does not stop the result upload.

Browser and Operating System Configurations

To route a result to a Test Plan configuration, include its numeric configuration ID in the case name or classname:

<testcase
  name="[TC-42] Login should succeed [config-id-9]"
  classname="Authentication"
  time="0.4" />

The CLI recognizes config-id-9, config-9, and [config-id-9]. The value is a TestCollab configuration ID, not the position of a browser or operating system in your test matrix.

Common JUnit XML Problems

The XML is not well formed

Use UTF-8, close every non-self-closing element, quote attributes, and escape reserved characters. For example, write &amp; for an ampersand in normal XML text. Prefer your framework's reporter over hand-written XML.

The result uploads but does not match a case

Add an explicit [TC-42] marker to the test name or classname. A number elsewhere in ordinary prose is deliberately not treated as a case ID.

A testcase has no usable name

Give every <testcase> a non-empty name. For existing-plan mode, the name or classname must also contain the TestCollab ID. Otherwise the CLI reports it as unresolved and cannot update that case.

Failures appear as passed

Make sure <failure> or <error> is inside the matching <testcase>. Suite-level failure counters alone do not identify which case failed.

Your framework creates many XML files

--result-file accepts one file path, not a directory or glob. Configure the reporter to produce one combined file or merge the reports before calling tc report.

Upload JUnit XML to TestCollab

Install the CLI and expose your API token as TESTCOLLAB_TOKEN. In the standard workflow, tc createTestPlan creates a plan, your test command generates results.xml, and tc report uploads that file. If you already have a plan ID, run:

npm install -g @testcollab/cli

tc report \
  --project 45 \
  --test-plan-id 123 \
  --format junit \
  --result-file ./results.xml

For a zero-setup import that creates missing suites, cases, a folder, and a plan from the file:

tc report \
  --project 45 \
  --format junit \
  --result-file ./results.xml \
  --auto-create

For an EU-hosted account, add --api-url https://api-eu.testcollab.io. See Uploading Test Results Using TestCollab CLI for authentication, plan creation, and reporting options.

Last updated 2026-08-26.