109 lines
3.8 KiB
Python
109 lines
3.8 KiB
Python
import json
|
|
import pytest
|
|
import datetime
|
|
import os
|
|
import time
|
|
from pathlib import Path
|
|
|
|
|
|
class JSONReporter:
|
|
"""
|
|
Custom pytest plugin to generate JSON test reports.
|
|
Based on the specification in section 9.4 of descripcion.md.
|
|
"""
|
|
|
|
def __init__(self, config):
|
|
self.config = config
|
|
self.start_time = time.time()
|
|
self.results = {
|
|
"summary": {
|
|
"total": 0,
|
|
"passed": 0,
|
|
"failed": 0,
|
|
"skipped": 0,
|
|
"error": 0,
|
|
"duration": 0,
|
|
"timestamp": datetime.datetime.now().isoformat(),
|
|
},
|
|
"tests": [],
|
|
}
|
|
|
|
def pytest_runtest_logreport(self, report):
|
|
"""Handle the reporting of a test run."""
|
|
if report.when == "call" or (report.when == "setup" and report.skipped):
|
|
self.results["summary"]["total"] += 1
|
|
|
|
if report.passed:
|
|
result = "passed"
|
|
self.results["summary"]["passed"] += 1
|
|
elif report.failed:
|
|
if report.when != "call":
|
|
result = "error"
|
|
self.results["summary"]["error"] += 1
|
|
else:
|
|
result = "failed"
|
|
self.results["summary"]["failed"] += 1
|
|
elif report.skipped:
|
|
result = "skipped"
|
|
self.results["summary"]["skipped"] += 1
|
|
|
|
# Extract test metadata
|
|
test_module = report.nodeid.split("::")[0]
|
|
test_class = report.nodeid.split("::")[1] if "::" in report.nodeid else None
|
|
test_name = report.nodeid.split("::")[-1]
|
|
|
|
# Extract error details if present
|
|
error_message = None
|
|
error_trace = None
|
|
if hasattr(report, "longrepr") and report.longrepr:
|
|
if hasattr(report.longrepr, "reprcrash") and report.longrepr.reprcrash:
|
|
error_message = report.longrepr.reprcrash.message
|
|
error_trace = str(report.longrepr)
|
|
|
|
# Add test result to list
|
|
self.results["tests"].append(
|
|
{
|
|
"id": report.nodeid,
|
|
"module": test_module,
|
|
"class": test_class,
|
|
"name": test_name,
|
|
"result": result,
|
|
"duration": report.duration,
|
|
"error_message": error_message,
|
|
"error_trace": error_trace,
|
|
}
|
|
)
|
|
|
|
def pytest_sessionfinish(self, session):
|
|
"""Generate report at end of test session."""
|
|
self.results["summary"]["duration"] = time.time() - self.start_time
|
|
|
|
# Create output directory if it doesn't exist
|
|
output_dir = Path("test_reports")
|
|
output_dir.mkdir(exist_ok=True)
|
|
|
|
# Generate timestamp for file name
|
|
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
output_path = output_dir / f"test_results_{timestamp}.json"
|
|
|
|
# Write results to file
|
|
with open(output_path, "w", encoding="utf-8") as f:
|
|
json.dump(self.results, f, indent=2, ensure_ascii=False)
|
|
|
|
# Also create a symlink/copy to latest results
|
|
latest_path = output_dir / "test_results_latest.json"
|
|
if os.path.exists(latest_path):
|
|
os.remove(latest_path)
|
|
with open(latest_path, "w", encoding="utf-8") as f:
|
|
json.dump(self.results, f, indent=2, ensure_ascii=False)
|
|
|
|
print(f"\nJSON test report generated: {output_path}")
|
|
|
|
|
|
# Register the plugin
|
|
@pytest.hookimpl(trylast=True)
|
|
def pytest_configure(config):
|
|
"""Register the JSON reporter plugin."""
|
|
config._json_reporter = JSONReporter(config)
|
|
config.pluginmanager.register(config._json_reporter, "json_reporter")
|