Skip to content

JUnit XML

JUnit XML is the lingua franca of CI test dashboards. Tales produces a Surefire-compatible XML file with one <testsuite> per .tales file and one <testcase> per scenario.

Terminal window
tales test ./e2e/pass --report-junit ./reports/junit.xml

The parent directory is created if missing. The output is fully self-contained, no external schema dependencies.

<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite name="e2e/pass/blog.tales"
tests="3" failures="0" skipped="0" time="1.232"
timestamp="2026-05-27T18:30:00Z">
<testcase name="Create blog post" classname="e2e/pass/blog.tales" time="0.842">
<!-- empty when passed -->
</testcase>
<testcase name="Failing scenario" classname="e2e/pass/blog.tales" time="0.123">
<failure message="status mismatch: want 201, got 500" type="assertion">
expected status 201 at scenario.create_user.expect.status
actual: 500 Internal Server Error
</failure>
</testcase>
<testcase name="Skipped scenario" classname="e2e/pass/blog.tales" time="0">
<skipped message="iOS tests require macOS and IOS_APP_PATH"/>
</testcase>
</testsuite>
</testsuites>
Tales conceptXML element
.tales file<testsuite name="<file path>">
Scenario<testcase name="<scenario name>" classname="<file path>">
Failed scenario<failure message="..." type="assertion">...</failure>
Skipped scenario<skipped message="<skip reason>"/>
Step durationaggregated into time="<seconds>" on the testcase
Teardown steprolled into the scenario’s duration (no separate testcase)

The <testsuite> element gains a skipped="N" attribute, and each skipped scenario emits a <skipped message="…"/> child on its <testcase>. This matches Surefire / JUnit5 conventions and shows up correctly in:

  • Jenkins JUnit plugin
  • GitLab CI test reports
  • CircleCI test summary
  • Bitbucket Pipelines test reports
  • Azure DevOps test runs

Mobile action records (tap, swipe, etc.) are intentionally not decomposed into <testcase> elements. They live in the JSONL stream and the visual HTML report instead, JUnit consumers don’t typically know what to do with sub-step data.

The most common pattern is to combine console (for live log scrolling) with JUnit (for the dashboard):

Terminal window
tales test ./e2e/pass \
--seed 1234 \
--report-junit ./reports/junit.xml

Then point your CI configuration at ./reports/junit.xml. Examples in CI integration.