behave-modern-json-report
A modern JSON report formatter for Behave that generates a rich, structured execution model for reporting, analytics, dashboards, AI, CI/CD pipelines and custom integrations.
Why?
The built-in Behave JSON formatter produces a flat, HTML-oriented structure. This project creates the canonical execution model for Behave — a schema-versioned, stable, extensible JSON format designed as an API, not as a rendering target.
It is the data foundation for:
- HTML / Markdown / Console reports
- AI-powered test analysis
- Dashboards and analytics platforms
- Historical execution comparison
- Trace viewers
- Custom integrations
Features
- Schema-versioned JSON output (
schemaVersion: "1.2.0") - Stable unique identifiers for every entity (execution, feature, scenario, step, attachment, error)
- Structured errors — type, message, traceback, location (never raw strings)
- Attachments — image, JSON, XML, HTML, PDF, video, text, binary; embedded or external
- Attachment helpers —
attach_file,attach_text,attach_json,attach_screenshot,logforenvironment.pyhooks - Step-level logs
- Gherkin backgrounds — shared background steps captured at feature, rule, and scenario level
- Rule support — Gherkin v6 / Behave 1.3.x rules as first-class
Ruleentities with background, tags, location, and nested scenarios - Scenario outlines —
isOutlineandoutlineNamefields, plusexampleTagsfor tags on Example blocks - Example keyword — Gherkin v6
Examplekeyword recognised as scenario outline - Expanded statuses —
passed,failed,skipped,undefined,pending,untested,error,hook_error,cleanup_error,xfailed,xpassed - Rich statistics — pass rate, counts, duration, error count, total attachments/logs, slowest step, avg scenario, common exception type, per-tag breakdown
- Rich environment — Python, Behave, platform, OS, hostname, CI provider, cwd, command, user, CPU count, memory, git branch/commit/remote
- Arbitrary metadata — inject domain-specific context via
[behave.userdata]withmjr.*keys - Cucumber JSON format —
CucumberJSONFormatteroutputs de facto Cucumber JSON for compatibility with cucumber-reporting, ReportPortal, Jenkins plugins, and more - Zero Behave dependency in the serializer — the JSON model is portable
- JSON Schema validation with helpful error messages
- Configurable — pretty/compact, embed/exclude attachments, exclude passed scenarios
- Production-ready — 174 tests, lint, type-check, CI
Installation
pip install behave-modern-json-report[behave]
For validation support:
pip install behave-modern-json-report[validate]
For enhanced environment detection (memory info):
pip install behave-modern-json-report[env]
For development:
pip install behave-modern-json-report[dev]
Quick Start
As a Behave formatter (modern JSON)
# Short format name (via entry point)
behave --format modern-json --outfile report.json
# Full module path (always works)
behave --format behave_modern_json_report:ModernJSONFormatter --outfile report.json
As a Behave formatter (Cucumber JSON)
# Short format name (via entry point)
behave --format cucumber-json --outfile cucumber.json
# Full module path (always works)
behave --format behave_modern_json_report:CucumberJSONFormatter --outfile cucumber.json
The Cucumber JSON format is compatible with tools that consume Cucumber JSON reports:
- cucumber-reporting (Jenkins plugin)
- multiple-cucumber-html-reporter
- ReportPortal, Allure, and other CI/CD integrations
With metadata via behave.ini
[behave]
format = behave_modern_json_report:ModernJSONFormatter
outfile = report.json
[behave.userdata]
mjr.project_name = My Project
mjr.branch = dev
mjr.team = qa
mjr.environment = staging
mjr.build_id = 42
All keys prefixed with mjr. are automatically injected into the report's metadata block. mjr.project_name is used as the project name. The prefix is stripped in the output:
{
"execution": { "projectName": "My Project" },
"metadata": { "data": { "branch": "dev", "team": "qa", "environment": "staging", "build_id": "42" } }
}
You can also pass metadata via CLI:
behave --userdata "mjr.branch=hotfix,mjr.build_id=99" --format behave_modern_json_report:ModernJSONFormatter --outfile report.json
Programmatically
from behave_modern_json_report import serialize, SerializerOptions
from behave_modern_json_report.collector import Collector
collector = Collector(project_name="my-app", metadata={"branch": "main"})
# Feed Behave events to the collector...
# collector.start_feature(feature)
# collector.start_scenario(scenario)
# collector.start_step(step)
# collector.end_step(step)
# collector.end_scenario(scenario)
# collector.end_feature(feature)
report = collector.finalize()
json_str = serialize(report, options=SerializerOptions(pretty=True))
Validation
from behave_modern_json_report import validate_json
with open("report.json") as f:
result = validate_json(f.read())
if not result:
for error in result.errors:
print(f"{error.path}: {error.message}")
Attachments in environment.py
from behave_modern_json_report import attach_file, attach_screenshot, attach_text, attach_json, log
def after_step(context, step):
if step.status == "failed":
# Screenshot from Selenium, Playwright, bytes, or file path
attach_screenshot(context, context.driver, name="failure.png")
# Attach arbitrary text
attach_text(context, f"URL: {context.url}", name="url.txt")
# Attach JSON data
attach_json(context, {"url": context.url, "status": step.status})
# Attach a file from disk
attach_file(context, "/tmp/dump.html", name="page.html")
# Log a message to the step
log(context, f"Failure at {context.url}", level="ERROR")
JSON Structure
{
"schemaVersion": "1.2.0",
"execution": {
"executionId": "exec-...",
"projectName": "my-app",
"startTime": "2026-06-30T14:20:00.000Z",
"endTime": "2026-06-30T14:20:01.500Z",
"duration": 1.5,
"status": "passed",
"command": "behave ...",
"workingDirectory": "/home/user/project"
},
"statistics": {
"features": 1,
"scenarios": 3,
"steps": 12,
"passed": 12,
"failed": 0,
"skipped": 0,
"undefined": 0,
"pending": 0,
"passRate": 1.0,
"duration": 1.5,
"errorCount": 0,
"totalAttachments": 0,
"totalLogs": 0,
"slowestStepDuration": 0.2,
"avgScenarioDuration": 0.5,
"commonExceptionType": null,
"byTag": {
"smoke": { "count": 3, "duration": 1.5, "passed": 3, "failed": 0 }
}
},
"environment": {
"pythonVersion": "3.12.3",
"behaveVersion": "1.2.6",
"platform": "linux",
"os": "Linux",
"osVersion": "6.5.0",
"hostname": "build-agent-01",
"ciProvider": "github-actions",
"cwd": "/home/user/project",
"command": "behave --format modern-json",
"user": "tester",
"cpuCount": 8,
"memoryMb": 16384,
"gitBranch": "main",
"gitCommit": "abc1234",
"gitRemote": "origin"
},
"features": [
{
"id": "feature-...",
"name": "Calculator",
"tags": ["smoke"],
"status": "passed",
"duration": 1.5,
"scenarios": [
{
"id": "scenario-...",
"name": "Add two numbers",
"featureId": "feature-...",
"status": "passed",
"duration": 0.5,
"steps": [
{
"id": "step-...",
"keyword": "Given",
"text": "I have entered 5 into the calculator",
"status": "passed",
"duration": 0.1
}
]
}
],
"rules": [
{
"id": "rule-...",
"name": "Addition",
"featureId": "feature-...",
"status": "passed",
"duration": 0.3,
"scenarios": []
}
]
}
],
"metadata": {
"data": {
"browser": "Chrome",
"environment": "QA",
"branch": "main"
}
}
}
See examples/golden-report.json for a complete example.
Configuration
SerializerOptions controls the output:
from behave_modern_json_report import SerializerOptions
opts = SerializerOptions(
pretty=True, # Indented JSON
include_environment=True, # Include environment block
include_attachments=True, # Include attachment metadata
embed_attachments=True, # Embed attachment content inline
exclude_passed_scenarios=False, # Drop passed scenarios (failure-only)
indent=2, # Indentation level
sort_keys=False, # Sort keys lexicographically
ensure_ascii=False, # Escape non-ASCII characters
)
Architecture
Behave Events → Collector → Execution Model → Serializer → JSON
Only collector.py and formatter.py depend on Behave. The model, serializer, validator and statistics modules are pure Python.
See docs/architecture.md for details.
Documentation
Project Structure
behave-modern-json-report/
├── behave_modern_json_report/
│ ├── __init__.py
│ ├── formatter.py # Modern JSON Formatter API entrypoint
│ ├── cucumber_formatter.py # Cucumber JSON Formatter API entrypoint
│ ├── cucumber_serializer.py # Model → Cucumber JSON (no Behave dep)
│ ├── collector.py # Behave events → model (only Behave dep)
│ ├── serializer.py # Model → JSON (no Behave dep)
│ ├── schema.py # Schema version constants
│ ├── validator.py # JSON Schema + runtime validation
│ ├── models.py # Execution model dataclasses
│ ├── statistics.py # Statistics aggregator
│ ├── environment.py # Runtime environment detection
│ ├── attach.py # High-level attachment helpers for hooks
│ ├── utils.py # IDs, timing, status, MIME helpers
│ └── schemas/
│ └── execution.schema.json
├── examples/
│ ├── behave_project/ # Real behave project example
│ │ ├── behave.ini
│ │ ├── run.py
│ │ └── features/
│ ├── calculator.feature
│ └── golden-report.json
├── docs/
├── tests/
├── .github/
│ └── workflows/
│ ├── ci.yml
│ └── release.yml
├── pyproject.toml
├── Makefile
├── README.md
├── LICENSE
└── CHANGELOG.md
Testing
python -m pytest tests/ -v
Test suites:
- Unit tests — utils, statistics, environment, attachments
- Schema validation tests — golden report, invalid reports
- Serialization tests — all model fields, options, backgrounds, rules
- Cucumber serializer tests — status mapping, embeddings, output, backgrounds, outlines
- Regression tests — collector lifecycle, formatter output, status aggregation, serialization filtering,
_overall_status - Golden JSON tests — structural stability
License
MIT — see LICENSE.
Release files for behave-modern-json-report 1.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| behave_modern_json_report-1.2.0.tar.gz | 52.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| behave_modern_json_report-1.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 92.0 kB
Release files / behave_modern_json_report-1.2.0.tar.gz
| Download URL | behave_modern_json_report-1.2.0.tar.gz |
|---|---|
| Size | 52.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f2f1d626fd27a5311a01e6567e8c5b612e58b4dcba8ef364678b3788b9962c52
|
|
BLAKE2b-256 checksum How to use checksums |
852258a16d0bfd00033e748fab313766eecf9b03201a91147af457deaa6e5cca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 10, 2026.
Transparency logRelease files / behave_modern_json_report-1.2.0-py3-none-any.whl
| Download URL | behave_modern_json_report-1.2.0-py3-none-any.whl |
|---|---|
| Size | 39.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
114a79a9a63ebe02a061be4ca52da5bede46cc878154edd50a8bb4943420e220
|
|
BLAKE2b-256 checksum How to use checksums |
3ea216b8f01be0e988807f396b010f39b966b9d103ce636cbca25e71e0557bc6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 10, 2026.
Transparency log