behave-format
July 2, 2026 · View on GitHub
The opinionated formatter for Behave
.featurefiles.
Black is for Python. gofmt is for Go. behave-format is for Gherkin.
Documentation: https://mathiaspaulenko.github.io/behave-format/
Overview
behave-format is a deterministic, opinionated formatter for Behave .feature files. It consumes the canonical domain model from behave-model and produces clean, consistent, beautifully formatted output.
Key principle: behave-format does NOT parse Gherkin. It does NOT lint. It does NOT validate. It ONLY transforms a behave-model.Project into formatted .feature files.
.feature files → behave-model (domain model) → behave-format → formatted .feature files
Features
- Opinionated — minimal configuration, sensible defaults
- Deterministic — same input always produces same output
- Idempotent —
format(format(x)) == format(x) - Fast — handles thousands of feature files efficiently
- CI-friendly —
--checkmode with exit code 1 when formatting is needed - Safe — never changes semantics (names, step text, table values, docstrings)
Installation
pip install behave-format
Quick Start
CLI
# Format files in place (default)
behave-format features/
# Check mode (CI) — exit 1 if formatting is needed
behave-format --check features/
# Diff mode — show differences without writing
behave-format --diff features/
Python API
from behave_model import load_project
from behave_format import format_project, render_project, Settings
project = load_project("features/")
# Format the project model in place
format_project(project)
# Or render to text
text = render_project(project, Settings())
Formatting Rules
Tags
Tags are sorted alphabetically by default:
@api @smoke
Features
- One blank line before each Feature
- Clean title formatting
Scenarios
- One blank line before each Scenario
- Two-space indentation for steps
Given user exists
When user logs in
Then dashboard is shown
Tables
Tables are always aligned:
Before:
|user|password|
|john|123|
After:
| user | password |
| john | 123 |
Blank Lines
- No trailing blank lines
- No multiple consecutive empty lines
- Consistent spacing between blocks
Indentation
- Spaces only (no tabs)
- Default: 2 spaces
Configuration
Minimal configuration via pyproject.toml:
[tool.behave-format]
indent = 2
sort_tags = true
sort_features = false
sort_scenarios = false
line_length = 120
| Option | Default | Description |
|---|---|---|
indent | 2 | Number of spaces for indentation |
sort_tags | true | Sort tags alphabetically |
sort_features | false | Sort features by name |
sort_scenarios | false | Sort scenarios by name |
line_length | 120 | Maximum line length (reference) |
Before / After
Before
@smoke @auth
Feature: Login
As a user
I want to log in
Background:
Given a database connection
@happy
Scenario: Successful login
Given the user is on the login page
When the user enters "admin" and "password"
Then the user should be logged in
After
@auth @smoke
Feature: Login
As a user
I want to log in
Background:
Given a database connection
@happy
Scenario: Successful login
Given the user is on the login page
When the user enters "admin" and "password"
Then the user should be logged in
Architecture
behave_format/
├── config/
│ └── settings.py # Settings dataclass + pyproject.toml loader
├── pipeline/
│ ├── normalize.py # Whitespace, indentation, tag normalization
│ ├── sort.py # Sort tags, features, scenarios
│ ├── align.py # Table alignment, trailing whitespace
│ ├── rules.py # Formatting rules registry
│ └── formatter.py # Main orchestrator (format_project)
├── printer/
│ ├── feature_printer.py
│ ├── scenario_printer.py
│ ├── step_printer.py
│ ├── table_printer.py
│ └── tag_printer.py
└── cli/
└── main.py # CLI entry point
Pipeline
- Normalize — clean whitespace, standardize indentation, normalize tags
- Sort — order tags (alphabetically by default), optionally features and scenarios
- Align — align table columns, remove trailing spaces
- Print — convert
behave-model→.featuretext (deterministic)
Safety
The formatter NEVER changes semantics:
- Feature names: preserved
- Scenario names: preserved
- Step text: preserved (only whitespace normalized)
- DocString content: preserved
- Table values: preserved (only alignment changes)
- Comments content: preserved
Integration
behave-format integrates naturally with the Behave ecosystem:
- behave-model — single source of truth
- behave-lint — linting
- behave-modern-json-report
- behave-modern-report
- behave-markdown-report
Development
pip install -e ".[dev]"
pytest tests/ -v
ruff check .
ruff format --check .
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Run
ruff check .andpytest tests/before submitting - Open a Pull Request
License
MIT