Skip to main content

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.

CI Python 3.11+ License: MIT Schema Version


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 helpersattach_file, attach_text, attach_json, attach_screenshot, log for environment.py hooks
  • 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 Rule entities with background, tags, location, and nested scenarios
  • Scenario outlinesisOutline and outlineName fields, plus exampleTags for tags on Example blocks
  • Example keyword — Gherkin v6 Example keyword recognised as scenario outline
  • Expanded statusespassed, 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] with mjr.* keys
  • Cucumber JSON formatCucumberJSONFormatter outputs 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:

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)

Source distribution for behave-modern-json-report 1.2.0
File Size Uploaded
behave_modern_json_report-1.2.0.tar.gz 52.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for behave-modern-json-report 1.2.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

1.2.0 This release

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page