Coverage Rules (BD301-304)¶
Detect unused step definitions, undefined feature steps, unused tags, and orphan scenarios. These rules ensure that every step in your feature files has a matching implementation and every step definition is actually used.
BD301 — unused-step-def¶
Finds step definitions that are never matched by any feature step. Unused definitions are dead code — they add noise, increase maintenance burden, and can mislead developers into thinking a step is tested when it isn't.
| Attribute | Value |
|---|---|
| Severity | warning |
| Category | Coverage |
| Configurable | No |
What it detects¶
# features/steps/auth.py
@given("the user clicks submit") # never used in any .feature file
def step_impl(context):
...
Example output¶
How to fix¶
- Remove the step definition if it's truly dead code.
- Add a feature step that exercises it if it was supposed to be tested.
Metadata¶
| Key | Value |
|---|---|
pattern |
The step pattern string. |
matcher |
The matcher type (parse, re, etc.). |
BD302 — undefined-step¶
Finds feature steps with no matching step definition. These steps will
raise UndefinedStepError when Behave runs them — the test will fail or be
skipped silently.
| Attribute | Value |
|---|---|
| Severity | error |
| Category | Coverage |
| Configurable | No |
What it detects¶
# features/login.feature
Scenario: Login
Given the database is seeded # no @given("the database is seeded") exists
Example output¶
How to fix¶
Create a step definition that matches the pattern:
Or fix the step text in the feature file to match an existing definition.
Metadata¶
| Key | Value |
|---|---|
pattern |
The step text as it appears in the feature file. |
keyword |
The step keyword (Given, When, Then). |
BD303 — unused-tag¶
Finds tags that appear only once in the entire project. Tags used only once cannot serve as reusable CI filters — they're effectively just labels on a single scenario.
Tags listed in exclude_tags are skipped.
| Attribute | Value |
|---|---|
| Severity | info |
| Category | Coverage |
| Configurable | exclude_tags (list) |
Configuration¶
Tags in exclude_tags are considered "intentionally unused" and will not
be flagged by BD303.
Example output¶
How to fix¶
- Remove the tag if it's not needed.
- Apply the tag to other scenarios if it represents a real category.
- Add the tag to
exclude_tagsif it's intentionally unique.
BD304 — orphan-scenario¶
Finds scenarios whose tags are all unique to that scenario (each tag appears on only one scenario). Such scenarios are effectively untagged for filtering purposes — no tag filter will select them along with other scenarios.
Scenarios with no tags are skipped (see BD202 for that).
| Attribute | Value |
|---|---|
| Severity | warning |
| Category | Coverage |
| Configurable | No |
What it detects¶
# This scenario has tags, but each tag appears on only this one scenario
@checkout @guest
Scenario: Guest checkout
...
If @checkout and @guest don't appear on any other scenario, running
behave --tags @checkout will only select this one scenario — defeating
the purpose of tagging.
Example output¶
BD304 WARNING Scenario "Guest checkout" has only unique tags ['@checkout', '@guest']
(features/checkout.feature:5)
How to fix¶
- Share tags across scenarios — use
@checkouton all checkout-related scenarios so--tags @checkoutselects them as a group. - Remove unique tags if they don't serve a filtering purpose.
- Accept the orphan status if the scenario genuinely needs a unique tag
(e.g.
@manualfor scenarios that can only be run manually).