---
title: Uploading JUnit test report files to Datadog
description: Datadog, the leading service for cloud-scale monitoring.
breadcrumbs: >-
  Docs > Test Optimization in Datadog > Configure Test Optimization > Uploading
  JUnit test report files to Datadog
---

# Uploading JUnit test report files to Datadog

{% callout %}
# Important note for users on the following Datadog sites: app.ddog-gov.com

{% alert level="danger" %}
This product is not supported for your selected [Datadog site](https://docs.datadoghq.com/getting_started/site.md). ().
{% /alert %}

{% /callout %}

{% alert level="danger" %}
**Note**: Datadog recommends the native instrumentation of tests over uploading JUnit XML files, as the native instrumentation provides more accurate time results, supports distributed traces on integration tests and other features that are not available with JUnit XML uploads. See the [Supported Features](https://docs.datadoghq.com/continuous_integration/tests.md#supported-features) table for more details.
{% /alert %}

## Overview{% #overview %}

JUnit test report files are XML files that contain test execution information, such as test and suite names, pass or fail status, duration, and sometimes error logs. Although introduced by the [JUnit](https://junit.org/junit5/) testing framework, many other popular frameworks are able to output results using this format.

If your testing framework can generate JUnit XML test reports, you can use these as a lightweight alternative to [instrumenting your tests natively](https://docs.datadoghq.com/continuous_integration/tests.md#setup) using Datadog SDKs. Test results imported from JUnit XML reports appear alongside test data reported by tracers.

## Compatibility{% #compatibility %}

Supported Datadog SDK:

| Datadog Library | Version    |
| --------------- | ---------- |
| `datadog-ci`    | \>= 2.17.0 |

## Installing the Datadog CI CLI{% #installing-the-datadog-ci-cli %}

Install the [`datadog-ci`](https://www.npmjs.com/package/@datadog/datadog-ci) CLI globally using `npm`:

```shell
npm install -g @datadog/datadog-ci
```

### Standalone binary{% #standalone-binary %}

If installing Node.js in the CI is an issue, standalone binaries are provided with [Datadog CI releases](https://github.com/DataDog/datadog-ci/releases). Only *linux-x64*, *linux-arm64*, *darwin-x64*, *darwin-arm64* (MacOS) and *win-x64* (Windows) are supported. To install, run the following from your terminal:

{% tab title="Linux" %}

```shell
curl -L --fail "https://github.com/DataDog/datadog-ci/releases/latest/download/datadog-ci_linux-x64" --output "/usr/local/bin/datadog-ci" && chmod +x /usr/local/bin/datadog-ci
```

Then run any command with `datadog-ci`:

```shell
datadog-ci version
```

{% /tab %}

{% tab title="MacOS" %}

```shell
curl -L --fail "https://github.com/DataDog/datadog-ci/releases/latest/download/datadog-ci_darwin-x64" --output "/usr/local/bin/datadog-ci" && chmod +x /usr/local/bin/datadog-ci
```

Then run any command with `datadog-ci`:

```shell
datadog-ci version
```

{% /tab %}

{% tab title="Windows" %}

```powershell
Invoke-WebRequest -Uri "https://github.com/DataDog/datadog-ci/releases/latest/download/datadog-ci_win-x64" -OutFile "datadog-ci.exe"
```

Then run any command with `Start-Process -FilePath "datadog-ci.exe"`:

```powershell
Start-Process -FilePath "./datadog-ci.exe" -ArgumentList version
```

{% /tab %}

## Uploading test reports{% #uploading-test-reports %}

To upload your JUnit XML test reports to Datadog, run the following command, specifying the name of the service or library that was tested using the `--service` parameter, and one or more file paths to either the XML report files directly or directories containing them:

```shell
datadog-ci junit upload --service <service_name> <path> [<path> ...]
```

Specify a valid [Datadog API key](https://app.datadoghq.com/organization-settings/api-keys) in the `DATADOG_API_KEY` environment variable, and the environment where tests were run (for example, `local` when uploading results from a developer workstation, or `ci` when uploading them from a CI provider) in the `DD_ENV` environment variable. For example:

```

DD_ENV=ci DATADOG_API_KEY=<api_key> DATADOG_SITE= datadog-ci junit upload \
  --service my-api-service \
  unit-tests/junit-reports e2e-tests/single-report.xml
```

{% alert level="danger" %}
Make sure that this command runs in your CI even when your tests have failed. Usually, when tests fail, the CI job aborts execution, and the upload command does not run.
{% /alert %}

{% tab title="GitHub Actions" %}
Use the [Status check functions](https://docs.github.com/en/actions/learn-github-actions/expressions#always):

```yaml
steps:
  - name: Run tests
    run: ./run-tests.sh
  - name: Upload test results to Datadog
    if: always()
    run: datadog-ci junit upload --service service_name ./junit.xml
```

{% /tab %}

{% tab title="GitLab" %}
Use the [`after_script` section](https://docs.gitlab.com/ee/ci/yaml/#after_script):

```yaml
test:
  stage: test
  script:
    - ./run-tests.sh
  after_script:
    - datadog-ci junit upload --service service_name ./junit.xml
```

{% /tab %}

{% tab title="Jenkins" %}
Use the [`post` section](https://www.jenkins.io/doc/book/pipeline/syntax/#post):

```groovy
pipeline {
  agent any
  stages {
    stage('Run tests') {
      steps {
        sh './run-tests.sh'
      }
      post {
        always {
          sh 'datadog-ci junit upload --service service_name ./junit.xml'
        }
      }
    }
  }
}
```

{% /tab %}

{% tab title="Bash" %}
If your CI system allows sub-shells:

```shell
$(./run-tests.sh); export tests_exit_code=$?
datadog-ci junit upload --service service_name ./junit.xml
if [ $tests_exit_code -ne 0 ]; then exit $tests_exit_code; fi
```

{% /tab %}

{% tab title="CircleCI" %}
Use the [`when` attribute](https://circleci.com/docs/configuration-reference/#the-when-attribute):

```yaml
steps:
  - run:
      name: Run tests
      command: ./run-tests.sh
  - run:
      name: Upload test results to Datadog
      when: always
      run: datadog-ci junit upload --service service_name ./junit.xml
```

{% /tab %}

Reports larger than 250 MiB may not be processed completely, resulting in missing tests or logs. For the best experience, ensure that the reports are under 250 MiB.

## Configuration settings{% #configuration-settings %}

This is the full list of options available when using the `datadog-ci junit upload` command:

{% dl %}

{% dt %}
`--service` (Required)
{% /dt %}

{% dd %}
Name of the service or library under test.**Environment variable**: `DD_SERVICE`**Example**: `my-api-service`
{% /dd %}

{% dt %}
`--env`
{% /dt %}

{% dd %}
Environment where tests were run.**Environment variable**: `DD_ENV`**Example**: `ci`
{% /dd %}

{% dt %}
`--tags`
{% /dt %}

{% dd %}
Key-value pairs in the form `key:value` to be attached to all tests (the `--tags` parameter can be specified multiple times). When specifying tags using `DD_TAGS`, separate them using commas (for example, `team:backend,priority:high`).**Environment variable**: `DD_TAGS`**Default**: (none)**Example**: `team:backend`**Note**: Tags specified using `--tags` and with the `DD_TAGS` environment variable are merged. If the same key appears in both `--tags` and `DD_TAGS`, the value in the environment variable `DD_TAGS` takes precedence.
{% /dd %}

{% dt %}
`--measures`
{% /dt %}

{% dd %}
Key-value numerical pairs in the form `key:number` to be attached to all tests (the `--measures` parameter can be specified multiple times). When specifying measures using `DD_MEASURES`, separate them using commas (for example, `memory_allocations:13,test_importance:2`).**Environment variable**: `DD_MEASURES`**Default**: (none)**Example**: `memory_allocations:13`**Note**: Measures specified using `--measures` and with the `DD_MEASURES` environment variable are merged. If the same key appears in both `--measures` and `DD_MEASURES`, the value in the environment variable `DD_MEASURES` takes precedence.
{% /dd %}

{% dt %}
`--report-tags`
{% /dt %}

{% dd %}
Key-value pairs in the form `key:value`. Works like the `--tags` parameter but these tags are only applied at the session level and are **not** merged with the environment variable `DD_TAGS`**Default**: (none)**Example**: `test.code_coverage.enabled:true`
{% /dd %}

{% dt %}
`--report-measures`
{% /dt %}

{% dd %}
Key-value pairs in the form `key:123`. Works like the `--measures` parameter but these tags are only applied at the session level and are **not** merged with the environment variable `DD_MEASURES`**Default**: (none)**Example**: `test.code_coverage.lines_pct:82`
{% /dd %}

{% dt %}
`--xpath-tag`
{% /dt %}

{% dd %}
Key and xpath expression in the form `key=expression`. These provide a way to customize tags for test in the file (the `--xpath-tag` parameter can be specified multiple times).See Providing metadata with XPath expressions for more details on the supported expressions.**Default**: (none)**Example**: `test.suite=/testcase/@classname`**Note**: Tags specified using `--xpath-tag` and with `--tags` or `DD_TAGS` environment variable are merged. xpath-tag gets the highest precedence, as the value is usually different for each test.
{% /dd %}

{% dt %}
`--logs`
{% /dt %}

{% dd %}
Enable forwarding content from the XML reports as [Logs](https://docs.datadoghq.com/logs.md). The content inside `<system-out>`, `<system-err>`, and `<failure>` is collected as logs. Logs from elements inside a `<testcase>` are automatically connected to the test.**Environment variable**: `DD_CIVISIBILITY_LOGS_ENABLED`**Default**: `false`**Note**: Logs are billed separately from Test Optimization.
{% /dd %}

{% dt %}
`--max-concurrency`
{% /dt %}

{% dd %}
The number of concurrent uploads to the API.**Default**: `20`
{% /dd %}

{% dt %}
`--dry-run`
{% /dt %}

{% dd %}
Runs the command without actually uploading the file to Datadog. All other checks are performed.**Default**: `false`
{% /dd %}

{% dt %}
`--skip-git-metadata-upload`
{% /dt %}

{% dd %}
Boolean flag used to skip git metadata upload.**Default**: `false`
{% /dd %}

{% dt %}
`--git-repository-url`
{% /dt %}

{% dd %}
The repository URL to retrieve git metadata from. If it is not passed, the URL is retrieved from the local git repository.**Default**: local git repository**Example**: `git@github.com:DataDog/documentation.git`
{% /dd %}

{% dt %}
`--verbose`
{% /dt %}

{% dd %}
Flag used to add extra verbosity to the output of the command**Default**: `false`
{% /dd %}

{% dt %}
Positional arguments
{% /dt %}

{% dd %}
The file paths or directories in which the JUnit XML reports are located. If you pass a directory, the CLI looks for all `.xml` files in it.
{% /dd %}

{% /dl %}

For more information about `service` and `env` reserved tags, see [Unified Service Tagging](https://docs.datadoghq.com/getting_started/tagging/unified_service_tagging.md).

The following environment variables are supported:

{% dl %}

{% dt %}
`DATADOG_API_KEY` (Required)
{% /dt %}

{% dd %}
[Datadog API key](https://app.datadoghq.com/organization-settings/api-keys) used to authenticate the requests.**Default**: (none)
{% /dd %}

{% /dl %}

Additionally, configure the Datadog site to use the selected one ():

{% dl %}

{% dt %}
`DATADOG_SITE` (Required)
{% /dt %}

{% dd %}
The [Datadog site](https://docs.datadoghq.com/getting_started/site.md) to upload results to.**Default**: `datadoghq.com`**Selected site**: 
{% /dd %}

{% /dl %}

## Collecting Git metadata{% #collecting-git-metadata %}

Datadog uses Git information for visualizing your test results and grouping them by repository, branch, and commit. Git metadata is automatically collected by the test instrumentation from CI provider environment variables and the local `.git` folder in the project path, if available.

If you are running tests in non-supported CI providers or with no `.git` folder, you can set the Git information manually using environment variables. These environment variables take precedence over any auto-detected information. Set the following environment variables to provide Git information:

{% dl %}

{% dt %}
`DD_GIT_REPOSITORY_URL`
{% /dt %}

{% dd %}
URL of the repository where the code is stored. Both HTTP and SSH URLs are supported.**Example**: `git@github.com:MyCompany/MyApp.git`, `https://github.com/MyCompany/MyApp.git`
{% /dd %}

{% dt %}
`DD_GIT_BRANCH`
{% /dt %}

{% dd %}
Git branch being tested. Leave empty if providing tag information instead.**Example**: `develop`
{% /dd %}

{% dt %}
`DD_GIT_TAG`
{% /dt %}

{% dd %}
Git tag being tested (if applicable). Leave empty if providing branch information instead.**Example**: `1.0.1`
{% /dd %}

{% dt %}
`DD_GIT_COMMIT_SHA`
{% /dt %}

{% dd %}
Full commit hash.**Example**: `a18ebf361cc831f5535e58ec4fae04ffd98d8152`
{% /dd %}

{% dt %}
`DD_GIT_COMMIT_MESSAGE`
{% /dt %}

{% dd %}
Commit message.**Example**: `Set release number`
{% /dd %}

{% dt %}
`DD_GIT_COMMIT_AUTHOR_NAME`
{% /dt %}

{% dd %}
Commit author name.**Example**: `John Smith`
{% /dd %}

{% dt %}
`DD_GIT_COMMIT_AUTHOR_EMAIL`
{% /dt %}

{% dd %}
Commit author email.**Example**: `john@example.com`
{% /dd %}

{% dt %}
`DD_GIT_COMMIT_AUTHOR_DATE`
{% /dt %}

{% dd %}
Commit author date in ISO 8601 format.**Example**: `2021-03-12T16:00:28Z`
{% /dd %}

{% dt %}
`DD_GIT_COMMIT_COMMITTER_NAME`
{% /dt %}

{% dd %}
Commit committer name.**Example**: `Jane Smith`
{% /dd %}

{% dt %}
`DD_GIT_COMMIT_COMMITTER_EMAIL`
{% /dt %}

{% dd %}
Commit committer email.**Example**: `jane@example.com`
{% /dd %}

{% dt %}
`DD_GIT_COMMIT_COMMITTER_DATE`
{% /dt %}

{% dd %}
Commit committer date in ISO 8601 format.**Example**: `2021-03-12T16:00:28Z`
{% /dd %}

{% /dl %}

## Collecting environment configuration metadata{% #collecting-environment-configuration-metadata %}

Datadog uses special dedicated tags to identify the configuration of the environment in which tests run, including the operating system, runtime, and device information, if applicable. When the same test for the same commit runs in more than one configuration (for example, on Windows and on Linux), the tags are used to differentiate the test in failure and flakiness detection.

You can specify these special tags using the `--tags` parameter when calling `datadog-ci junit upload`, or by setting the `DD_TAGS` environment variable.

All of these tags are optional, and only the ones you specify will be used to differentiate between environment configurations.

{% dl %}

{% dt %}
`os.platform`
{% /dt %}

{% dd %}
Name of the operating system.**Examples**: `windows`, `linux`, `darwin`
{% /dd %}

{% dt %}
`os.version`
{% /dt %}

{% dd %}
Version of the operating system.**Examples**: `10.15.4`, `14.3.2`, `95`
{% /dd %}

{% dt %}
`os.architecture`
{% /dt %}

{% dd %}
Architecture of the operating system.**Examples**: `x64`, `x86`, `arm64`
{% /dd %}

{% dt %}
`runtime.name`
{% /dt %}

{% dd %}
Name of the language interpreter or programming runtime.**Examples**: `.NET`, `.NET Core`, `OpenJDK Runtime Environment`, `Java(TM) SE Runtime Environment`, `CPython`
{% /dd %}

{% dt %}
`runtime.version`
{% /dt %}

{% dd %}
Version of the runtime.**Examples**: `5.0.0`, `3.1.7`
{% /dd %}

{% dt %}
`runtime.vendor`
{% /dt %}

{% dd %}
Name of the runtime vendor where applicable. For example, when using a Java runtime.**Examples**: `AdoptOpenJDK`, `Oracle Corporation`
{% /dd %}

{% dt %}
`runtime.architecture`
{% /dt %}

{% dd %}
Architecture of the runtime.**Examples**: `x64`, `x86`, `arm64`
{% /dd %}

{% /dl %}

For mobile apps (Swift, Android):

{% dl %}

{% dt %}
`device.model`
{% /dt %}

{% dd %}
The model of the device being tested.**Examples**: `iPhone11,4`, `AppleTV5,3`
{% /dd %}

{% dt %}
`device.name`
{% /dt %}

{% dd %}
The name of the device being tested.**Examples**: `iPhone 12 Pro Simulator`, `iPhone 13 (QA team)`
{% /dd %}

{% /dl %}

## Adding code owners{% #adding-code-owners %}

To add [codeowners](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) information to your JUnit XML tests, you can use the [GitHub integration](https://docs.datadoghq.com/integrations/github.md) to read the `CODEOWNERS` file in your repository or provide some additional information manually.

As a result, the JUnit XML tests have a `test.codeowners` tag with the owner of those tests.

### Using the GitHub integration{% #using-the-github-integration %}

To automatically add the `test.codeowners` tag to your tests, you need to:

1. Have a `CODEOWNERS` file [in one of the allowed locations](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-file-location) in your repository.

1. Provide the tests source file in your JUnit XML report. The following plugins do this automatically and add the `file` attribute to the `<testcase>` or `<testsuite>` elements in the XML report:

   - phpunit
   - Most Python plugins (pytest, unittest)
   - Most Ruby plugins (ruby minitest)

If the XML does not have the `file` attribute, you need to provide the source file manually. Example of a valid report:

```xml
  <?xml version="1.0" encoding="UTF-8"?>
  <testsuite name="suite">
    <testcase name="test_with_file" file="src/commands/junit" />
  </testsuite>
  
```
Enable the [GitHub app](https://docs.github.com/developers/apps/getting-started-with-apps/about-apps). If you do not have a GitHub app, follow the steps in the next section. If you already have a GitHub app, enable the `Contents: Read` permission so Datadog can read the `CODEOWNERS` file. Once enabled, wait a few minutes for the changes to take effect.
**Note:** Github is the only supported Git provider.

#### Configure a GitHub App{% #configure-a-github-app %}

The JUnit XML uses a private [GitHub App](https://docs.github.com/developers/apps/getting-started-with-apps/about-apps) to read the `CODEOWNERS` file.

1. Go to the [GitHub integration tile](https://app.datadoghq.com/integrations/github/).
1. Click **Link GitHub Account**.
1. Follow the instructions to configure the integration for a personal or organization account.
1. In **Edit Permissions**, grant `Contents: Read` access.
1. Click **Create App in GitHub** to finish the app creation process on GitHub.
1. Give the app a name, for example, `Datadog Test Optimization`.
1. Click **Install GitHub App** and follow the instructions on GitHub.

#### Manually providing the `test.source.file` tag{% #manually-providing-the-testsourcefile-tag %}

For those plugins that do not provide the `file` attribute in the XML report, you can provide the `test.source.file` tag. There is no need to provide the exact path to a specific file, [you can use any syntax you would use in the CODEOWNERS file](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-syntax) such as `src/myTeamFolder` or `*.md`.

There are multiple ways to provide the `test.source.file` tag:

- Using the `--tags` parameter or the `DD_TAGS` environment variable.

  ```shell
  datadog-ci junit upload --service service-name --tags test.source.file:src/myTeamFolder my_report.xml
  ```

This adds the `test.source.file` tag to all the tests in the report. All of the tests will have the same owner(s).

- If you want to provide different source files for the same XML report, you can use property elements or set the `file` attribute manually to individual `<testcase>` or `<testsuite>` elements.

## Providing metadata with XPath expressions{% #providing-metadata-with-xpath-expressions %}

In addition to the `--tags` CLI parameter and the `DD_TAGS` environment variable, which apply custom tags globally to all tests included the uploaded XML report, the `--xpath-tag` parameter provides custom rules to add tags from different attributes within the XML to each test.

The parameter provided must have the format `key=expression`, where `key` is the name of the custom tag to be added and `expression` is a valid [XPath](https://www.w3schools.com/xml/xpath_syntax.asp) expression within the ones supported.

While XPath syntax is used for familiarity, only the following expressions are supported:

{% dl %}

{% dt %}
`/testcase/@attribute-name`
{% /dt %}

{% dd %}
The XML attribute from `<testcase attribute-name="value">`.
{% /dd %}

{% dt %}
`/testcase/../@attribute-name`
{% /dt %}

{% dd %}
The XML attribute from the parent `<testsuite attribute-name="value">` of the current `<testcase>`.
{% /dd %}

{% dt %}
`/testcase/..//property[@name='property-name']`
{% /dt %}

{% dd %}
The `value` attribute from the `<property name="property-name" value="value">` inside the parent `<testsuite>` of the current `<testcase>`.
{% /dd %}

{% dt %}
`/testcase//property[@name='property-name']`
{% /dt %}

{% dd %}
The `value` attribute from the `<property name="property-name" value="value">` inside the current `<testcase>`.
{% /dd %}

{% /dl %}

Examples:

{% tab title="Test suite from @classname" %}
By default, the `test.suite` tag of the tests is read from `<testsuite name="suite name">`. However, some plugins might report a better value in `<testcase classname="TestSuite">`.

To change `test.suite` tags from `value 1`, `value 2` to `SomeTestSuiteClass`, `OtherTestSuiteClass`:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
  <testsuite tests="1" failures="0" time="0.030000" name="value 1">
    <testcase classname="SomeTestSuiteClass" name="test_something" time="0.030000"></testcase>
  </testsuite>
  <testsuite tests="1" failures="0" time="0.021300" name="value 2">
    <testcase classname="OtherTestSuiteClass" name="test_something" time="0.021300"></testcase>
  </testsuite>
</testsuites>
```

```shell
datadog-ci junit upload --service service_name \
  --xpath-tag test.suite=/testcase/@classname ./junit.xml
```

{% /tab %}

{% tab title="Tag from attribute" %}
To add `custom_tag` to each test with values `value 1`, `value 2`:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
  <testsuite tests="1" failures="0" time="0.020000" name="SomeTestSuiteClass">
    <testcase name="test_something" time="0.010000" attr="value 1"></testcase>
    <testcase name="test_other" time="0.010000" attr="value 2"></testcase>
  </testsuite>
</testsuites>
```

```shell
datadog-ci junit upload --service service_name \
  --xpath-tag custom_tag=/testcase/@attr ./junit.xml
```

{% /tab %}

{% tab title="Tag from testsuite property" %}
To add `custom_tag` to each test with values `value 1`, `value 2`:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
  <testsuite tests="1" failures="0" time="0.030000" name="SomeTestSuiteClass">
    <properties>
      <property name="prop" value="value 1"></property>
    </properties>
    <testcase name="test_something" time="0.030000" attr="value 1"></testcase>
  </testsuite>
  <testsuite tests="1" failures="0" time="0.021300" name="OtherTestSuiteClass">
    <properties>
      <property name="prop" value="value 1"></property>
    </properties>
    <testcase name="test_something" time="0.021300" attr="value 1"></testcase>
  </testsuite>
</testsuites>
```

```shell
datadog-ci junit upload --service service_name \
  --xpath-tag custom_tag=/testcase/..//property[@name=\'prop\'] ./junit.xml
```

**Note:** The name must be in quotes. Bash requires quotes to be escaped using a backslash, for example `[@name='prop']` must be entered as `[@name='prop'].
{% /tab %}

{% alert level="danger" %}
When using bash from Git for Windows, define the **MSYS\_NO\_PATHCONV=1** environment variable. Otherwise, any argument starting with **/** will be expanded to a Windows path.
{% /alert %}

## Providing metadata through property elements{% #providing-metadata-through-property-elements %}

Another way to provide additional tags to specific tests is including `<property name="dd_tags[key]" value="value">` elements within the `<testsuite>` or `<testcase>` elements. If you add these tags to a `<testcase>` element, they are stored in its test span. If you add the tags to a `<testsuite>` element, they are stored in all of that suite's test spans.

To be processed, the `name` attribute in the `<property>` element must have the format `dd_tags[key]`, where `key` is the name of the custom tag to be added. Other properties are ignored.

**Example**: Adding tags to a `<testcase>` element.

```xml
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
  <testsuite tests="1" failures="0" time="0.030000" name="SomeTestSuiteClass">
    <testcase classname="SomeTestSuiteClass" name="test_something" time="0.010000">
      <properties>
        <property name="dd_tags[custom_tag]" value="some value"></property>
        <property name="dd_tags[runtime.name]" value="CPython"></property>
      </properties>
    </testcase>
  </testsuite>
</testsuites>
```

**Example**: Adding tags to a `<testsuite>` element.

```xml
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
  <testsuite tests="1" failures="0" time="0.030000" name="SomeTestSuiteClass">
    <properties>
      <property name="dd_tags[custom_tag]" value="some value"></property>
      <property name="dd_tags[runtime.name]" value="CPython"></property>
    </properties>
    <testcase classname="SomeTestSuiteClass" name="test_something" time="0.010000"></testcase>
  </testsuite>
</testsuites>
```

The values that you send to Datadog are strings, so the facets are displayed in lexicographical order. To send integers instead of strings, use the `--measures` flag and the `DD_MEASURES` environment variable.

## Reporting code coverage{% #reporting-code-coverage %}

{% alert level="warning" %}
This Test Optimization feature is legacy. Use the new dedicated [Code Coverage](https://docs.datadoghq.com/code_coverage.md) product instead.
{% /alert %}

It is possible to report code coverage for a given JUnit report via the `--report-measures` option, by setting the `test.code_coverage.lines_pct` measure:

```shell
datadog-ci junit upload --service my-api-service --report-measures test.code_coverage.lines_pct:82 unit-tests/junit-reports e2e-tests/single-report.xml
```

For more information, see [Code Coverage](https://docs.datadoghq.com/continuous_integration/tests/code_coverage.md?tab=junitreportuploads).

## Further reading{% #further-reading %}

- [Explore Test Results and Performance](https://docs.datadoghq.com/continuous_integration/tests.md)
- [Troubleshooting Test Optimization](https://docs.datadoghq.com/tests/troubleshooting.md)
