2. Working with Multiple Output Formatsο
Purpose: Learn to generate reports in different formats for different workflows
This tutorial shows you how to generate failure reports in JSON, Markdown, XML, CSV, and YAML formats. Each format serves different use cases in your development workflow.
2.1. What Youβll Learnο
How to generate reports in all 5 supported formats
When to use each format in your workflow
How to handle optional format dependencies
How to automate multi-format report generation
2.2. Prerequisitesο
Completed Getting Started with FailExtract
Understanding of different data formats (JSON, XML, CSV, etc.)
10 minutes of time
2.3. Format Overviewο
FailExtract supports 5 output formats, each optimized for different use cases:
Format |
Extension |
Best For |
Dependencies |
Core Feature |
|---|---|---|---|---|
JSON |
|
Automation, APIs, machine processing |
None |
β Yes |
Markdown |
|
Documentation, GitHub, human reading |
None |
β Yes |
XML |
|
Structured data, enterprise systems |
None |
β Yes |
CSV |
|
Spreadsheets, data analysis, Excel |
None |
β Yes |
YAML |
|
Configuration, Docker, CI/CD |
|
β Optional |
2.4. Setting Up Example Failuresο
Letβs create some realistic test failures to demonstrate format differences:
from failextract import extract_on_failure, FailureExtractor, OutputConfig
@extract_on_failure
def test_database_connection():
"""Simulate a database connection test failure."""
connection_string = "postgresql://user:pass@localhost:5432/testdb"
connected = False # Simulate connection failure
assert connected, f"Failed to connect to database: {connection_string}"
@extract_on_failure
def test_api_response():
"""Simulate an API response validation failure."""
api_response = {
'status': 'error',
'code': 500,
'message': 'Internal server error',
'data': None
}
assert api_response['status'] == 'success', f"API returned error: {api_response}"
@extract_on_failure
def test_file_processing():
"""Simulate a file processing failure."""
file_path = "/data/important_file.csv"
file_exists = False # Simulate missing file
assert file_exists, f"Required file not found: {file_path}"
2.5. Generate Reports in All Formatsο
Hereβs how to generate reports in every supported format:
def generate_all_formats():
"""Generate reports in all supported formats."""
extractor = FailureExtractor()
if not extractor.failures:
print("No failures to report")
return
# Format definitions: (format_name, description)
formats = [
("json", "Machine-readable JSON format"),
("markdown", "Markdown format for documentation"),
("xml", "XML format for structured data"),
("csv", "CSV format for spreadsheet analysis"),
("yaml", "YAML format for configuration-like output")
]
print(f"Generating reports for {len(extractor.failures)} failures...")
for format_name, description in formats:
try:
config = OutputConfig(f"failures.{format_name}", format=format_name)
extractor.save_report(config)
print(f"β Generated failures.{format_name} - {description}")
except Exception as e:
print(f"β Failed to generate {format_name}: {e}")
2.6. Complete Working Exampleο
Save this as multiple_formats_example.py:
#!/usr/bin/env python3
"""Multiple format generation example"""
from failextract import extract_on_failure, FailureExtractor, OutputConfig
@extract_on_failure
def test_database_connection():
connection_string = "postgresql://user:pass@localhost:5432/testdb"
connected = False
assert connected, f"Failed to connect to database: {connection_string}"
@extract_on_failure
def test_api_response():
api_response = {
'status': 'error', 'code': 500,
'message': 'Internal server error', 'data': None
}
assert api_response['status'] == 'success', f"API returned error: {api_response}"
@extract_on_failure
def test_file_processing():
file_path = "/data/important_file.csv"
file_exists = False
assert file_exists, f"Required file not found: {file_path}"
def run_tests_and_generate_reports():
# Run the failing tests
tests = [test_database_connection, test_api_response, test_file_processing]
for test_func in tests:
try:
test_func()
except AssertionError:
pass # Expected to fail
# Generate all format reports
extractor = FailureExtractor()
formats = ["json", "markdown", "xml", "csv", "yaml"]
for format_name in formats:
try:
config = OutputConfig(f"failures.{format_name}", format=format_name)
extractor.save_report(config)
print(f"β Generated failures.{format_name}")
except Exception as e:
print(f"β Failed to generate {format_name}: {e}")
if __name__ == "__main__":
run_tests_and_generate_reports()
Run the example:
python multiple_formats_example.py
Expected output:
β Generated failures.json
β Generated failures.markdown
β Generated failures.xml
β Generated failures.csv
β Failed to generate yaml: No module named 'yaml'
2.7. Understanding Each Formatο
JSON Format (failures.json)
Machine-readable, perfect for automation:
[
{
"test_name": "test_database_connection",
"exception_type": "AssertionError",
"exception_message": "Failed to connect to database: postgresql://user:pass@localhost:5432/testdb",
"timestamp": "2025-06-06T09:30:15.123456",
"local_variables": {
"connection_string": "postgresql://user:pass@localhost:5432/testdb",
"connected": false
}
}
]
Markdown Format (failures.markdown)
Human-readable, perfect for documentation:
# Test Failures Report
Generated on: 2025-06-06 09:30:15
## test_database_connection
**Exception:** AssertionError
**Message:** Failed to connect to database: postgresql://user:pass@localhost:5432/testdb
XML Format (failures.xml)
Structured data, perfect for enterprise systems:
<?xml version="1.0" encoding="UTF-8"?>
<testFailureReport>
<metadata>
<generated>2025-06-06T09:30:15.123456</generated>
<totalFailures>1</totalFailures>
</metadata>
<failures>
<failure>
<testName>test_database_connection</testName>
<module>__main__</module>
<file>/path/to/test.py</file>
<timestamp>2025-06-06T09:30:15.123456</timestamp>
<exceptionType>AssertionError</exceptionType>
<exceptionMessage>Failed to connect to database: postgresql://user:pass@localhost:5432/testdb</exceptionMessage>
<testSource><![CDATA[
- def test_database_connection():
connection_string = βpostgresql://user:pass@localhost:5432/testdbβ connected = False assert connected, fβFailed to connect to database: {connection_string}β
]]></testSource>
</failure>
</failures>
</testFailureReport>
CSV Format (failures.csv)
Tabular data, perfect for spreadsheet analysis:
Test Name,Module,File,Timestamp,Exception Type,Exception Message,Line Number
test_database_connection,__main__,/path/to/test.py,2025-06-06T09:30:15.123456,AssertionError,"Failed to connect to database: postgresql://user:pass@localhost:5432/testdb",
YAML Format (failures.yaml)
Configuration-style, perfect for CI/CD:
test_failure_report:
metadata:
generated: 2025-06-06T09:30:15.123456
total_failures: 1
failures:
- test_info:
name: test_database_connection
module: __main__
file: /path/to/test.py
timestamp: 2025-06-06T09:30:15.123456
exception:
type: AssertionError
message: "Failed to connect to database: postgresql://user:pass@localhost:5432/testdb"
test_source: |
def test_database_connection():
connection_string = "postgresql://user:pass@localhost:5432/testdb"
connected = False
assert connected, f"Failed to connect to database: {connection_string}"
2.8. Adding YAML Supportο
YAML requires an optional dependency. Install it with:
# Option 1: Install with YAML support
pip install failextract[formatters]
# Option 2: Install YAML library separately
pip install pyyaml
After installation, the YAML format will work without errors.
2.9. Workflow-Specific Format Recommendationsο
- Development Workflow
Use Markdown for quick human review and JSON for automation
- CI/CD Pipeline
Use JSON for parsing and CSV for artifact storage
- Bug Reports
Use Markdown for GitHub issues and JSON for detailed context
- Data Analysis
Use CSV for Excel/spreadsheet analysis
- Configuration Management
Use YAML for infrastructure-as-code integration
2.10. Handling Format Errors Gracefullyο
Always handle potential format generation errors:
def safe_format_generation():
extractor = FailureExtractor()
# Core formats (always available)
core_formats = ["json", "markdown", "xml", "csv"]
# Optional formats (may require dependencies)
optional_formats = ["yaml"]
# Generate core formats
for format_name in core_formats:
config = OutputConfig(f"failures.{format_name}", format=format_name)
extractor.save_report(config)
print(f"β Generated {format_name}")
# Try optional formats
for format_name in optional_formats:
try:
config = OutputConfig(f"failures.{format_name}", format=format_name)
extractor.save_report(config)
print(f"β Generated {format_name}")
except ImportError as e:
print(f"β Skipped {format_name}: {e}")
except Exception as e:
print(f"β Failed {format_name}: {e}")
2.11. Automating Multi-Format Reportsο
Create a utility function for consistent multi-format generation:
def create_comprehensive_report(base_filename="failures"):
"""Generate failure reports in all available formats."""
extractor = FailureExtractor()
if not extractor.failures:
print("No failures to report")
return []
generated_files = []
formats = ["json", "markdown", "xml", "csv", "yaml"]
for format_name in formats:
try:
filename = f"{base_filename}.{format_name}"
config = OutputConfig(filename, format=format_name)
extractor.save_report(config)
generated_files.append(filename)
print(f"β {filename}")
except Exception as e:
print(f"β Skipped {format_name}: {e}")
return generated_files
2.12. Next Stepsο
Now that you understand multiple formats, you can:
Configure Behavior: Configuring FailExtract - Customize output paths and format options
Integrate with pytest: Integrating FailExtract with pytest - Automate multi-format generation in test suites
Create Custom Formatters: Creating Custom Formatters - Build your own output formats
Set Up CI/CD: Learn how to automate format generation in your deployment pipeline
2.13. Key Takeawaysο
pip install failextract[formatters]You now have flexible reporting for any workflow!