# How to Configure Pytest JUnit XML Output for Gradle Integration

> Integrate Python tests into your JVM build pipeline. Learn best practices for configuring pytest JUnit XML output with Gradle for seamless CI integration.

- Repository: [pytest-dev/pytest](https://github.com/pytest-dev/pytest)
- Tags: how-to-guide
- Published: 2026-02-21

---

**Configure pytest to emit JUnit XML using the `--junit-xml` flag or `junit_xml` configuration key, set `junit_family = xunit2` for modern schema compatibility, and point Gradle to the output file via `test.reports.junitXml.destination` to integrate Python tests into your JVM build pipeline.**

The pytest framework generates JUnit-compatible XML reports that Gradle consumes natively, enabling unified test reporting across Python and Java/Kotlin projects. By configuring specific options in [`pytest.ini`](https://github.com/pytest-dev/pytest/blob/main/pytest.ini) or [`pyproject.toml`](https://github.com/pytest-dev/pytest/blob/main/pyproject.toml), you control the XML schema, suite naming, and metadata that appear in Gradle's test dashboards. This guide explains the architectural implementation in `pytest-dev/pytest` and provides production-ready configuration examples for CI pipelines.

## Architectural Overview: JUnit XML Generation in Pytest

Pytest implements JUnit XML generation through a dedicated plugin module that hooks into the test lifecycle. Understanding this architecture helps you optimize configuration for Gradle consumption.

In [`src/_pytest/junitxml.py`](https://github.com/pytest-dev/pytest/blob/main/src/_pytest/junitxml.py), the `pytest_addoption` hook registers the `--junit-xml` CLI option and related configuration keys (lines 379-386). When pytest initializes, the `pytest_configure` hook instantiates the `LogXML` class (lines 23-28), which stores all JUnit-related settings including output paths, suite names, and logging levels.

The `LogXML.__init__` method captures critical parameters such as `junit_family`, `junit_suite_name`, and `junit_prefix` (lines 58-68). During test execution, `LogXML` receives test reports via `pytest_runtest_logreport`, aggregating results into an internal tree structure. Finally, the `pytest_sessionfinish` hook triggers the XML serialization to disk (lines 39-46), producing the `<testsuites>` document that Gradle parses.

## Configuration Best Practices for Gradle Integration

Stable XML output requires explicit schema selection and deterministic file paths that Gradle can reliably consume across local and CI environments.

### Select the Modern Schema Version

Set `junit_family = xunit2` in your configuration to ensure Gradle compatibility. This generates the modern JUnit XML schema that Gradle's test reporter recognizes, avoiding deprecated attribute warnings.

```ini
[pytest]
junit_family = xunit2
junit_suite_name = PythonTests
junit_xml = build/reports/pytest/tests.xml

```

### Configure Metadata and Prefixes

Use `junit_prefix` to prepend package names to test class names, improving organization in Gradle reports. The `junit_suite_name` parameter defines the root `<testsuite>` name attribute, while `junit_logging` controls captured output inclusion.

```toml
[tool.pytest.ini_options]
junit_family = "xunit2"
junit_suite_name = "MyPythonTests"
junit_prefix = "com.example.python"
junit_xml = "build/reports/pytest/tests.xml"
junit_logging = "all"

```

### Command-Line Execution

For ad-hoc runs or CI scripts where configuration files are not available, use the CLI flags directly:

```bash
pytest --junit-xml=build/reports/pytest/tests.xml \
       --junit-prefix=com.example \
       --junit-suite-name=PythonIntegrationTests \
       --junit-logging=all

```

## Integrating Pytest into Gradle Builds

Execute pytest from Gradle using an `Exec` task or the Python plugin, ensuring the XML output lands in a directory Gradle monitors for test results.

### Basic Exec Task Configuration

Define a task that creates the output directory before execution and invokes pytest with the necessary JUnit parameters:

```groovy
task runPytest(type: Exec) {
    commandLine 'python', '-m', 'pytest',
                '--junit-xml=build/reports/pytest/tests.xml',
                '--junit-prefix=com.example',
                '--junit-logging=all'
    doFirst {
        file('build/reports/pytest').mkdirs()
    }
}

check.dependsOn runPytest

```

### Wiring Reports into Gradle's Test Task

Configure Gradle to recognize the pytest XML alongside Java test results by setting the JUnit XML destination:

```groovy
test {
    reports {
        junitXml {
            destination = file("$buildDir/reports/pytest")
        }
    }
    testLogging {
        events "passed", "failed", "skipped"
        showStandardStreams = true
    }
}

```

### Using the Gradle Python Plugin

For projects using the `org.gradle.python` plugin, configure a dedicated task with explicit module arguments:

```groovy
task pytest(type: PythonTask) {
    module = 'pytest'
    args = ['--junit-xml=build/reports/pytest/tests.xml',
            '--junit-suite-name=PythonTests',
            '--junit-prefix=com.example']
    doFirst {
        file('build/reports/pytest').mkdirs()
    }
}

check.dependsOn pytest

```

## Adding Custom Properties and Test Metadata

Pytest's `record_property` fixture injects custom `<property>` elements into the JUnit XML, enabling filtering and categorization in Gradle Enterprise or CI dashboards.

```python
def test_authentication_service(record_property):
    record_property("component", "auth-service")
    record_property("requirement", "AUTH-123")
    record_property("risk-level", "high")
    assert authenticate_user("valid_token")

```

These properties appear as key-value pairs within each `<testcase>` element in [`src/_pytest/junitxml.py`](https://github.com/pytest-dev/pytest/blob/main/src/_pytest/junitxml.py), allowing Gradle to aggregate tests by component or requirement ID without parsing Python source files.

## Key Source Files in Pytest

Understanding these files helps debug XML generation issues or contribute enhancements:

- **[`src/_pytest/junitxml.py`](https://github.com/pytest-dev/pytest/blob/main/src/_pytest/junitxml.py)**: Core implementation containing `LogXML` class, option registration, and XML serialization logic.
- **[`testing/test_junitxml.py`](https://github.com/pytest-dev/pytest/blob/main/testing/test_junitxml.py)**: Comprehensive test suite validating XML output against schema requirements.
- **`doc/en/reference/reference.rst`**: Official documentation for JUnit XML options and configuration keys.

## Summary

- **Configure XML output** using `junit_xml` in [`pytest.ini`](https://github.com/pytest-dev/pytest/blob/main/pytest.ini) or `--junit-xml` on the CLI to produce files Gradle consumes
- **Use `junit_family = xunit2`** to ensure modern schema compatibility with Gradle's test parser
- **Set `junit_suite_name` and `junit_prefix`** to organize results meaningfully in Gradle reports
- **Enable `junit_logging = all`** to capture stdout/stderr directly in the XML for faster debugging
- **Use `record_property`** to inject custom metadata for CI filtering and traceability
- **Execute via Gradle `Exec` tasks** and point `reports.junitXml.destination` to the pytest output directory

## Frequently Asked Questions

### How do I configure pytest to generate JUnit XML output compatible with Gradle?

Configure the `junit_xml` path in [`pytest.ini`](https://github.com/pytest-dev/pytest/blob/main/pytest.ini) or [`pyproject.toml`](https://github.com/pytest-dev/pytest/blob/main/pyproject.toml), set `junit_family = xunit2` for schema compatibility, and ensure the output directory matches Gradle's `reports.junitXml.destination`. Run pytest with `--junit-xml=build/reports/pytest/tests.xml` to generate the file Gradle expects.

### What is the difference between junit_family values in pytest?

The `xunit2` family (default in recent pytest versions) produces XML compliant with the modern JUnit schema used by Gradle and Jenkins. The legacy `xunit1` format uses older attribute names that may cause parsing warnings or missing test cases in contemporary CI systems.

### Can I add custom metadata to JUnit XML reports for Gradle filtering?

Yes. Use the `record_property` fixture in your test functions to inject `<property>` elements with custom key-value pairs. Gradle and CI tools can parse these properties to categorize tests by component, ticket ID, or risk level.

### How do I prevent path issues when running pytest from Gradle?

Ensure the output directory exists before pytest runs by adding `doFirst { file('build/reports/pytest').mkdirs() }` to your Gradle task. While pytest creates parent directories automatically, Gradle's file watchers may fail if the directory structure changes mid-build or if the path is outside the standard build tree.