JUnit XML format support for CI test reports
JUnit XML has a long history and many dialects. TestNod reads the elements and attributes that virtually every test runner produces, and anything else is ignored rather than rejected, so a report with vendor-specific extensions still uploads cleanly.
Document structure
TestNod accepts the standard <testsuites> and <testsuite> root forms. Both single-suite and multi-suite documents work:
<testsuites>
<testsuite name="UserSpec" tests="3" failures="1" errors="0" skipped="1" time="0.42">
<testcase classname="UserSpec" name="creates a user" time="0.10"/>
<testcase classname="UserSpec" name="rejects invalid emails" time="0.12">
<failure type="RSpec::Expectations::ExpectationNotMetError" message="expected: false">
...
</failure>
</testcase>
<testcase classname="UserSpec" name="future feature" time="0.00">
<skipped/>
</testcase>
</testsuite>
</testsuites>
Elements TestNod reads
| Element | What's used |
|---|---|
<testsuite> |
name becomes the suite name. time is read for suite duration when present, otherwise summed from cases. |
<testcase> |
classname and name together form the case identity used for diffs and failure clustering. time is the per-case duration. |
<failure> |
Marks the case as failed. type and message are stored and feed the failure signature. The element body is stored as the failure trace. |
<error> |
Marks the case as errored (test setup blew up rather than an assertion failing). Same fields as <failure>. |
<skipped> |
Marks the case as skipped. Skipped counts feed the skipped test count creep alert. |
<system-out>, <system-err> |
Stored as the test suite's captured output and shown on the test suite detail panel. |
<properties> |
Each child <property> is stored as a name/value pair and shown on the test suite detail panel. |
How TestNod resolves test status
Each <testcase> resolves to exactly one status, in this priority order:
<error>present → errored<failure>present → failed<skipped>present → skipped- Otherwise → passed
A case with both <failure> and <error> is recorded as errored.
Identity for diffs and clustering
Two test cases are "the same case across runs" when their classname and name match exactly, including whitespace. If your test runner mangles either value across runs (some Jest configurations include a path with a hash, for example), comparisons and clustering will treat them as different cases. The fix is to stabilize the identity in the runner, not in TestNod.
What's ignored
These show up in some dialects and are silently dropped:
- Custom attributes on
<testsuite>or<testcase>beyond the ones listed above. - Vendor-specific elements (Maven Surefire's
<rerunFailure>, for example). - XML comments and processing instructions.
Ignored fields do not cause the upload to fail. If you depend on a field that is currently ignored, email [email protected] and the TestNod team will look at supporting it.
Character encoding
TestNod expects UTF-8 input. Most runners default to UTF-8, but some Java tools emit UTF-16 with a BOM. Convert to UTF-8 before upload if you see encoding errors during parsing.