> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tester.army/llms.txt
> Use this file to discover all available pages before exploring further.

# Export to TestRail (JUnit XML)

> Download TesterArmy group runs as JUnit XML from the dashboard or the API and import them into TestRail with the TestRail CLI, or feed them to any CI test reporter.

TesterArmy renders any group run (batch) or single run as a JUnit XML report. The report is shaped for TestRail's official importer, `trcli parse_junit`, and works with every CI test reporter that reads JUnit (GitHub `dorny/test-reporter`, GitLab `artifacts:reports:junit`, Jenkins, Buildkite, CircleCI).

## Get the report

<Tabs>
  <Tab title="Dashboard">
    Open a group run under **Results**, open its actions menu, and choose **Download JUnit report**. The option appears once every run in the group has finished.
  </Tab>

  <Tab title="API">
    ```bash theme={"theme":"vesper"}
    curl -fsS -H "Authorization: Bearer $TESTERARMY_API_KEY" \
      "https://tester.army/api/v1/batches/$BATCH_ID/junit" -o testerarmy.xml
    ```

    `GET /api/v1/runs/{id}/junit` returns a one-case report for a single run (a group's preparation run answers `422`, since it is not a test case). Both endpoints answer `409` while runs are still queued or running; add `?allowIncomplete=true` to export anyway, with unfinished runs marked `skipped`. The batch ID comes back from `POST /v1/groups/{groupId}/runs` and from the group webhook response.
  </Tab>
</Tabs>

## Import into TestRail

Install the [TestRail CLI](https://github.com/gurock/trcli) and point it at the file:

```bash theme={"theme":"vesper"}
pip install trcli

trcli -y \
  -h https://<your-instance>.testrail.io \
  --project "My Product" \
  -u "$TESTRAIL_USER" -p "$TESTRAIL_API_KEY" \
  parse_junit \
  --title "TesterArmy - $GROUP_NAME - $CI_COMMIT_SHORT_SHA" \
  --case-matcher name \
  -f testerarmy.xml
```

`-y` lets trcli create cases that do not exist yet. Omit it and pass `-n` to reject unknown cases instead.

### Matching TesterArmy tests to TestRail cases

TestRail decides which case a result belongs to using `--case-matcher`:

| Matcher    | What it reads                                              | When to use it                                                                                                                                   |
| ---------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `name`     | Case ids in the test name, for example `[C123] Login flow` | You already have TestRail cases. Put the id in the TesterArmy test title and results attach to the existing case. Several ids: `[C123, C456]`.   |
| `property` | The `test_id` property                                     | Same as `name`; TesterArmy emits `test_id` automatically whenever the title contains bracketed case ids.                                         |
| `auto`     | `classname.name` as an `automation_id` custom field        | You want TestRail to create and track cases from TesterArmy. Requires the `automation_id` field in TestRail. Renaming a test creates a new case. |

The `classname` is the TesterArmy group name and the `name` is the test title, so with `auto` each group becomes a TestRail section and each test a case.

### What lands in TestRail

* **Status:** passing runs are Passed. A `FAILED` verdict is a `<failure>` and imports as Failed. A `BLOCKED` verdict, a run that crashed before a verdict, or a run that finished without one is an `<error>` (type `BLOCKED`, the run error code, or `MISSING_VERDICT`); TestRail imports errors as Failed, and the `testerarmy_result` property keeps the distinction. Cancelled, planner-skipped, and unfinished runs are `<skipped>`.
* **Comment:** the `testrail_result_comment` property holds the verdict, the agent's summary, a link to the run in the dashboard, and screenshot URLs.
* **Steps:** one `testrail_result_step` property per test step (`passed:<step title>`, `failed:<step title>`, or `untested:<step title>` for any other step status), so step results show up on the TestRail result when the case uses separated steps.
* **Elapsed:** the `time` attribute is the run duration in seconds.

Preparation runs that only warm a session for the group are not exported.

## Use it as a CI report

The same file works as a CI test report. Queue the group through `POST /api/v1/groups/{groupId}/runs`, keep the `batchId` from the response, and download the report once the batch has finished. On GitHub Actions:

```yaml theme={"theme":"vesper"}
- run: |
    mkdir -p reports
    curl -fsS -H "Authorization: Bearer $TESTERARMY_API_KEY" \
      "https://tester.army/api/v1/batches/$BATCH_ID/junit" -o reports/testerarmy.xml
  continue-on-error: true
- uses: dorny/test-reporter@v2
  if: always()
  with:
    name: TesterArmy
    path: reports/testerarmy.xml
    reporter: java-junit
```

On GitLab CI, declare the file under `artifacts: reports: junit:` so results render on the merge request.

## Troubleshooting

### `409 BatchNotFinished`

Runs are still queued or running. Wait for the batch, or add `?allowIncomplete=true` to export the unfinished runs as `skipped`.

### Every import creates new cases

You are using `--case-matcher auto` without an `automation_id` field, or the test titles changed. Switch to `--case-matcher name` and add `[C<id>]` to the TesterArmy test titles.

### Screenshots are links, not attachments

trcli only uploads attachments from local file paths. The report carries screenshot URLs in the result comment; download them in CI and add `testrail_attachment` properties yourself if you need inline attachments.
