Skip to main content

pytest-junit-logging

Test Code Quality PyPI version Python versions

A pytest plugin that captures log output during test execution and embeds it into JUnit XML reports.

Features

  • 📋 Log Integration: Automatically captures all log output during test execution and embeds it into JUnit XML reports
  • 🎯 Smart Scope Distribution: Session-scoped logs appear in all tests, module-scoped logs appear in module tests, function-scoped logs appear only in specific tests
  • Execution Phase Tags: Each log entry includes a step attribute indicating whether it occurred during setup or test execution
  • 🔧 Configurable Log Levels: Control verbosity with --junit-log-level option
  • 🚨 Assertion Capture: Automatically captures assertion failure messages as ASSERT-level logs
  • 📁 Relative Paths: File paths in logs are relative to project root for portability
  • 🔒 Safe HTML: Automatically escapes HTML entities in log messages

Installation

pip install pytest-junit-logging

Usage

The plugin activates automatically when you use pytest's --junit-xml option:

# Basic usage - captures all logs (DEBUG level and above)
pytest --junit-xml=results.xml

# Control log verbosity
pytest --junit-xml=results.xml --junit-log-level=WARNING

# Only capture errors and critical logs
pytest --junit-xml=results.xml --junit-log-level=ERROR

Command Line Options

Option Choices Default Description
--junit-log-level DEBUG, INFO, WARNING, ERROR, CRITICAL DEBUG Minimum log level to include in JUnit XML

Example

Test Code

# test_example.py
import logging
import pytest

@pytest.fixture(scope="session")
def database():
    logging.info("Setting up database connection")
    yield "db_connection"
    logging.info("Closing database connection")

@pytest.fixture
def user_data():
    logging.debug("Creating test user data")
    return {"id": 123, "name": "Test User"}

def test_user_creation(database, user_data):
    logging.info("Starting user creation test")
    logging.debug(f"Using database: {database}")
    logging.debug(f"User data: {user_data}")

    # Simulate test logic
    assert user_data["id"] == 123

    logging.info("User creation test completed successfully")

def test_user_validation(database, user_data):
    logging.warning("This is a validation test")
    assert user_data["name"] == "Test User"

Generated JUnit XML

pytest test_example.py --junit-xml=results.xml --junit-log-level=INFO

The resulting results.xml will include embedded logs:

<?xml version='1.0' encoding='utf-8'?>
<testsuites name="pytest tests">
    <testsuite name="pytest" errors="0" failures="0" skipped="0" tests="2" time="0.01">
        <testcase classname="test_example" name="test_user_creation" time="0.001">
            <logs>
                <log step="setup" ts="2025-11-27T10:00:00.000000+00:00" level="INFO" src="test_example.py:8">Setting up database connection</log>
                <log step="test" ts="2025-11-27T10:00:00.100000+00:00" level="INFO" src="test_example.py:15">Starting user creation test</log>
                <log step="test" ts="2025-11-27T10:00:00.200000+00:00" level="INFO" src="test_example.py:22">User creation test completed successfully</log>
            </logs>
        </testcase>
        <testcase classname="test_example" name="test_user_validation" time="0.001">
            <logs>
                <log step="setup" ts="2025-11-27T10:00:01.000000+00:00" level="INFO" src="test_example.py:8">Setting up database connection</log>
                <log step="test" ts="2025-11-27T10:00:01.100000+00:00" level="WARNING" src="test_example.py:25">This is a validation test</log>
                <log step="test" ts="2025-11-27T10:00:01.200000+00:00" level="INFO" src="test_example.py:10">Closing database connection</log>
            </logs>
        </testcase>
    </testsuite>
</testsuites>

Log Scope Distribution

The plugin intelligently distributes logs based on fixture scopes:

  • Session-scoped fixture logs: Appear in all test cases
  • Module-scoped fixture logs: Appear in all tests within the same module
  • Function-scoped fixture logs: Appear only in the specific test that uses the fixture
  • Test execution logs: Appear only in their respective test case

Execution Phase Tags

Each log entry includes a step attribute that indicates during which execution phase the log was generated:

  • step="setup": Logs from fixture setup (session, module, function fixture initialization)
  • step="test": Logs from test execution and fixture teardown

This makes it easy to distinguish between infrastructure logs (fixture setup) and actual test logic:

<logs>
    <log step="setup" ts="..." level="INFO" src="conftest.py:10">Database connection established</log>
    <log step="setup" ts="..." level="INFO" src="conftest.py:25">User fixtures initialized</log>
    <log step="test" ts="..." level="DEBUG" src="test_user.py:15">Starting user validation test</log>
    <log step="test" ts="..." level="INFO" src="test_user.py:20">User validation completed</log>
    <log step="test" ts="..." level="INFO" src="conftest.py:30">Cleaning up user fixtures</log>
</logs>

Post-processing: To flatten the structure and remove step attributes for legacy compatibility, simply strip the step="..." attributes from log elements.

Log Level Filtering

Control the verbosity of logs in your JUnit XML:

# Capture everything (default)
pytest --junit-xml=results.xml --junit-log-level=DEBUG

# Only warnings and above
pytest --junit-xml=results.xml --junit-log-level=WARNING

# Only errors and critical
pytest --junit-xml=results.xml --junit-log-level=ERROR

Note: ASSERT level logs (from assertion failures) are always included regardless of the log level filter.

Integration with CI/CD

The plugin works seamlessly with CI/CD systems that consume JUnit XML reports:

  • GitHub Actions: Use with dorny/test-reporter
  • Jenkins: Native JUnit plugin support
  • GitLab CI: Built-in JUnit report processing
  • Azure DevOps: Test result publishing

Requirements

  • Python 3.10+
  • pytest 6.0+

Development

Setup Development Environment

# Clone the repository
git clone <repository-url>
cd pytest-junit-logging

# Install in development mode with dev dependencies
pip install -e ".[dev]"

Running Tests

Local Testing

# Run tests for current Python version
pytest

# Run tests across all supported Python/pytest combinations (requires all Python versions)
tox

# Run specific tox environment
tox -e py312-pytest8

Docker-based Testing (Recommended)

# Run all tests across all Python versions in Docker
./scripts/test-all.sh

# Test specific Python version
./scripts/test-single.sh 3.11 8    # Python 3.11 with pytest 8.x

# Test individual components with docker-compose
docker-compose -f docker-compose.test.yml run --rm test-py310    # Python 3.10 tests
docker-compose -f docker-compose.test.yml run --rm test-quality  # Linting & coverage
docker-compose -f docker-compose.test.yml run --rm test-mindeps  # Minimum dependencies

Tox Test Matrix

The project tests against these combinations:

Python pytest 6.x pytest 7.x pytest 8.x
3.10
3.11 -
3.12 - -
3.13 - -

Code Quality

Manual Code Quality Checks

# Format code
black src/ tests/

# Lint code
ruff check src/ tests/

# Type checking
mypy src/

Pre-commit Hooks (Recommended)

Install pre-commit to automatically run quality checks before commits:

# Install pre-commit
pip install pre-commit

# Install the git hook scripts
pre-commit install

# (Optional) Run against all files
pre-commit run --all-files

Once installed, pre-commit will automatically run:

  • Black (code formatting)
  • Ruff (linting with auto-fixes)
  • MyPy (type checking)
  • Bandit (security scanning)
  • Basic file checks (trailing whitespace, YAML validation, etc.)

This ensures all committed code meets quality standards and matches the CI pipeline checks.

CI/CD and Releases

Automated Testing

The project uses GitHub Actions for comprehensive CI/CD:

  • Test Matrix: Runs all tests across Python 3.10-3.13 and pytest 6.x-8.x combinations
  • Code Quality: Automated linting (ruff), formatting (black), and type checking (mypy)
  • Integration Tests: End-to-end testing with real pytest runs and XML validation
  • Security Scanning: Automated security scans with bandit
  • Coverage Reporting: Code coverage tracking and reporting

All checks must pass before code can be merged to main.

Releases

Releases are fully automated:

  1. Create a git tag with version format vX.Y.Z (e.g., v1.0.0, v2.1.3)
  2. Push the tag to trigger the release workflow:
    git tag v1.0.0
    git push origin v1.0.0
    
  3. Automated pipeline runs:
    • Full test suite across all supported versions
    • Code quality checks
    • Package building and validation
    • PyPI publishing (using trusted publishing)
    • GitHub release creation with changelog

Version Management

The project uses setuptools-scm for automatic version management:

  • Version is automatically derived from git tags
  • No manual version bumping required
  • Supports semantic versioning and pre-releases (e.g., v1.0.0-beta1)

License

MIT License - see LICENSE file for details.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytest_junit_logging-0.1.0.tar.gz (34.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pytest_junit_logging-0.1.0-py3-none-any.whl (11.9 kB view details)

Uploaded Python 3

File details

Details for the file pytest_junit_logging-0.1.0.tar.gz.

File metadata

  • Download URL: pytest_junit_logging-0.1.0.tar.gz
  • Upload date:
  • Size: 34.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pytest_junit_logging-0.1.0.tar.gz
Algorithm Hash digest
SHA256 03b22edd822d0c010df65def56e3a577314be3c2af8b8b93c11e9612f55acf0c
MD5 3155af8357576cf4f52eaf53563dd496
BLAKE2b-256 8b1d437ab81482d43a98151d5eae820cb896d440d162a56b9fb0187ad56391c7

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_junit_logging-0.1.0.tar.gz:

Publisher: release.yml on Mewongu/pytest-junit-logging

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytest_junit_logging-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pytest_junit_logging-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1ece2266ba1d2491da4c155c25410fcc9d5c8979426b9439e1ec7cc5ca0ab1dc
MD5 0086efeceec0809b397b6bcbc4cad2a6
BLAKE2b-256 c9332dae619380a9dd28f6504c6e1ee1969a5509e75fd80e994e9296961ba72f

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_junit_logging-0.1.0-py3-none-any.whl:

Publisher: release.yml on Mewongu/pytest-junit-logging

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page