CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-unittest-xml-reporting

unittest-based test runner with Ant/JUnit like XML reporting.

Overview
Eval results
Files

jenkins-compatibility.mddocs/

Jenkins Compatibility Tools

Transformation utilities for ensuring XML report compatibility with various versions of Jenkins xUnit plugins. Addresses schema validation differences and provides tools for adapting reports to specific CI/CD requirements.

Capabilities

XML Report Transformation

Transform XML reports to be compatible with specific versions of Jenkins xUnit plugin that have stricter schema validation requirements.

def transform(xml_data):
    """
    Transform XML report for Jenkins xUnit plugin compatibility.
    
    Removes attributes that cause validation failures in Jenkins xUnit plugin 
    version 1.104+ which uses stricter XSD validation.
    
    Parameters:
    - xml_data: bytes, input XML report data
    
    Returns:
    - bytes: transformed XML report data
    
    Dependencies:
    - lxml: required for XML transformation
    """

Jenkins Plugin Compatibility

Jenkins JUnit Plugin

The standard JUnit plugin (https://plugins.jenkins.io/junit/) has relaxed validation:

  • Schema Validation: None (at time of writing)
  • Compatibility: Standard unittest-xml-reporting output works without modification
  • Attributes: Accepts all attributes including file, line, timestamp
import unittest
import xmlrunner

# Standard output works with Jenkins JUnit plugin
unittest.main(
    testRunner=xmlrunner.XMLTestRunner(output='test-reports'),
    failfast=False, buffer=False, catchbreak=False
)

Jenkins xUnit Plugin v1.100

Older version with more lenient XSD validation:

  • Schema: Uses relaxed junit-10.xsd
  • Compatibility: Standard unittest-xml-reporting output works
  • Validation: Performs XSD validation but accepts additional attributes

Jenkins xUnit Plugin v1.104+

Newer version with strict XSD validation:

  • Schema: Uses strict junit-10.xsd
  • Compatibility: Requires transformation to remove unsupported attributes
  • Validation: Strict XSD validation fails on extra attributes

Usage Examples

Basic Transformation for Jenkins xUnit v1.104+

import io
import unittest
import xmlrunner
from xmlrunner.extra.xunit_plugin import transform

# Generate XML report in memory
output = io.BytesIO()
unittest.main(
    testRunner=xmlrunner.XMLTestRunner(output=output),
    failfast=False, buffer=False, catchbreak=False, exit=False
)

# Transform for Jenkins xUnit plugin compatibility
transformed_xml = transform(output.getvalue())

# Write to file for Jenkins consumption
with open('TEST-report.xml', 'wb') as report:
    report.write(transformed_xml)

CI/CD Pipeline Integration

# ci_test_runner.py
import io
import sys
import unittest
import xmlrunner
from xmlrunner.extra.xunit_plugin import transform

def run_tests_for_jenkins():
    """Run tests and generate Jenkins-compatible XML reports."""
    # Capture XML output
    output = io.BytesIO()
    
    # Run tests with XML reporting
    runner = xmlrunner.XMLTestRunner(
        output=output,
        verbosity=2,
        elapsed_times=True
    )
    
    # Discover and run tests
    loader = unittest.TestLoader()
    suite = loader.discover('tests', pattern='test_*.py')
    result = runner.run(suite)
    
    # Transform for Jenkins compatibility
    xml_data = output.getvalue()
    transformed_xml = transform(xml_data)
    
    # Write Jenkins-compatible report
    with open('junit-report.xml', 'wb') as report_file:
        report_file.write(transformed_xml)
    
    # Return exit code based on test results
    return 0 if result.wasSuccessful() else 1

if __name__ == '__main__':
    sys.exit(run_tests_for_jenkins())

Batch File Transformation

import os
import glob
from xmlrunner.extra.xunit_plugin import transform

def transform_reports_directory(input_dir, output_dir):
    """Transform all XML reports in a directory for Jenkins compatibility."""
    os.makedirs(output_dir, exist_ok=True)
    
    for xml_file in glob.glob(os.path.join(input_dir, 'TEST-*.xml')):
        # Read original report
        with open(xml_file, 'rb') as f:
            xml_data = f.read()
        
        # Transform for compatibility
        transformed_xml = transform(xml_data)
        
        # Write transformed report
        filename = os.path.basename(xml_file)
        output_path = os.path.join(output_dir, filename)
        with open(output_path, 'wb') as f:
            f.write(transformed_xml)
        
        print(f"Transformed {xml_file} -> {output_path}")

# Usage
transform_reports_directory('raw-reports', 'jenkins-reports')

Transformation Details

Removed Attributes

The transformation removes the following attributes that cause validation failures:

  • testcase/@file: Source file path
  • testcase/@line: Source line number
  • testcase/@timestamp: Individual test timestamp

Preserved Elements

All other elements and attributes are preserved:

  • testsuite attributes (name, tests, failures, errors, skipped, time, timestamp)
  • testcase attributes (classname, name, time)
  • failure/error/skipped elements and their attributes
  • system-out/system-err content
  • properties elements

XSLT Implementation

The transformation uses XSLT (Extensible Stylesheet Language Transformations):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="xml" indent="yes" />
    
    <!-- Remove problematic attributes -->
    <xsl:template match="//testcase/@file" />
    <xsl:template match="//testcase/@line" />
    <xsl:template match="//testcase/@timestamp" />
    
    <!-- Copy everything else -->
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*" />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Error Handling

Dependency Requirements

The transformation requires lxml:

try:
    from xmlrunner.extra.xunit_plugin import transform
except ImportError:
    print("lxml is required for Jenkins compatibility transformation")
    print("Install with: pip install lxml")
    sys.exit(1)

Invalid XML Handling

from lxml import etree
from xmlrunner.extra.xunit_plugin import transform

def safe_transform(xml_data):
    """Safely transform XML with error handling."""
    try:
        return transform(xml_data)
    except etree.XMLSyntaxError as e:
        print(f"Invalid XML input: {e}")
        return xml_data  # Return original on error
    except Exception as e:
        print(f"Transformation error: {e}")
        return xml_data

Integration Strategies

Conditional Transformation

import os
from xmlrunner.extra.xunit_plugin import transform

def maybe_transform_for_jenkins(xml_data):
    """Transform XML only if Jenkins xUnit plugin version requires it."""
    jenkins_version = os.getenv('JENKINS_XUNIT_VERSION', '1.100')
    
    if jenkins_version >= '1.104':
        return transform(xml_data)
    return xml_data

Custom Transformation Pipeline

from xmlrunner.extra.xunit_plugin import transform
import lxml.etree as etree

def custom_jenkins_transform(xml_data, remove_timestamps=True, 
                           remove_file_info=True):
    """Custom transformation with configurable options."""
    if not (remove_timestamps or remove_file_info):
        return xml_data
    
    # Parse XML
    doc = etree.XML(xml_data)
    
    # Remove attributes based on options
    if remove_file_info:
        for elem in doc.xpath('//testcase'):
            elem.attrib.pop('file', None)
            elem.attrib.pop('line', None)
    
    if remove_timestamps:
        for elem in doc.xpath('//testcase'):
            elem.attrib.pop('timestamp', None)
    
    # Return transformed XML
    return etree.tostring(doc, pretty_print=True, encoding='UTF-8')

This transformation system ensures unittest-xml-reporting works seamlessly with various Jenkins plugin versions while maintaining all essential test result information.

Install with Tessl CLI

npx tessl i tessl/pypi-unittest-xml-reporting

docs

core-runner.md

django-integration.md

index.md

jenkins-compatibility.md

result-collection.md

xml-utilities.md

tile.json