| name | bdd-scenarios |
| description | Use when writing or reviewing behave/Gherkin scenarios for jBOM — covering the review checklist, colon-consistency rule, step parameterization patterns, annotated examples, and the dynamic test-data builder pattern that must be applied to every scenario in the jBOM BDD suite. |
bdd-scenarios
Actionable guidance for writing and reviewing jBOM behave scenarios.
Apply the 24 BDD axioms consistently
across the suite. This skill covers: how to run the suite, how to apply each
tier of axioms, what anti-patterns look like next to correct alternatives, and
the mechanical review checklist to run before committing any scenario.
Running the suite
python -m behave --format progress
python -m behave --tags @regression
python -m behave features/bom/bom_generation.feature
Always run the full suite before opening or merging a PR. Merges are blocked
when any scenario is failed, errored, or undefined.
Colon-consistency rule (Axiom #7)
Gherkin requires a colon at the end of a step that introduces a table or
docstring block, and forbids it on standalone statements. behave's step-pattern
matching is sensitive to this: a missing or extra colon causes an "undefined
step" error even when the matching function exists.
Correct:
Given the "TestBoard" schematic contains components:
| Reference | Value | Footprint |
| R1 | 10K | R_0603_1608 |
And a KiCad project named "ComponentTest"
Incorrect — missing colon before table:
Given a schematic with components
| Reference | Value | Footprint |
| R1 | 10K | R_0603_1608 |
Incorrect — unnecessary colon on standalone statement:
Given a KiCad project named "ComponentTest":
Apply this rule to every Given, When, and Then step. When adding a new step
that takes a table, ensure the step-definition regex ends with :? or : as
appropriate, and update all callers to be consistent.
Step parameterization (Axiom #15)
Step definitions must accept parameters rather than hardcoding values.
Parameterized (correct):
@when('I generate a BOM with --{fabricator:w} fabricator')
def step_generate_bom_with_fabricator(context, fabricator):
...
Hardcoded (wrong):
@when('I generate a BOM with JLC fabricator')
def step_generate_bom_jlc_only(context):
...
Use behave's built-in type converters ({name:w}, {value:d}, {value:f})
to capture typed parameters. Prefer generic steps that the caller specializes
via the scenario text rather than one step per concrete value.
Service-API testing via the CLI surface (Axiom #4)
jBOM's testing pattern follows the codebase's layered design: the CLI is a
thin wrapper over the service API, and the plugin (when present) accesses the
same service API. BDD scenarios exercise the CLI — which by design exercises
the service API — and that single path is the right level for behavioral
coverage.
The correct pattern:
# One scenario per business behavior, exercised through the CLI.
Scenario: BOM generation matches inventory components by category and value
The step implementation invokes the CLI (python -m jbom.cli.main via
common_steps.py). The CLI is thin; what is actually under test is the
service API behind it. Plugin coverage, when the plugin is exercised, goes
through the same service API and is tested separately at the plugin boundary.
Anti-pattern to avoid: writing three near-identical scenarios for CLI,
API, and Plugin paths.
# Wrong — multi-modal duplication
Scenario: Generate BOM via API
Scenario: Generate BOM via CLI
Scenario: Generate BOM via Plugin
An earlier formulation of Axiom #4 framed this as a "multi-modal" step
definition that dispatched to all three execution paths simultaneously. That
approach was tried and rejected because it expanded combinatoric test
complexity while only exercising mocks rather than the in-situ paths.
Multi-modal step dispatch is not a current priority — the CLI-surface BDD
strategy is what is exercised today.
Concrete test vectors (Axiom #2)
Every scenario must use literal, measurable values:
- Priority:
0, 1, 5, 50, 100, 2147483647
- Part numbers:
C25804, RC0603FR-0710K
- Quantities: specific integers
- Tolerance values:
1%, 5%, 10%
Prohibited abstractions: "high priority parts", "component matches",
"various values", "some inventory items".
Explicit preconditions and dynamic test data (Axioms #18, #19)
Every condition that makes the scenario's outcome inevitable must appear in
Given steps. Negative conditions — what is absent from the inventory — must be
stated explicitly. Use the three-tier builder pattern to balance completeness
with DRY:
Tier 1 — Background (feature-wide foundation):
Background: Base Test Data Foundation
Given a clean test environment
And a base inventory with standard components:
| IPN | Category | Value | Package |
| R001 | RES | 10K | 0603 |
| R002 | RES | 1K1 | 0603 |
Tier 2 — Dynamic extensions (scenario-specific):
Given the schematic is extended with component:
| Reference | Value | Package |
| R1 | 1K | 0603 |
And the inventory excludes exact match for "1K 0603 resistor"
Tier 3 — Named fixtures (complex scenarios):
Given the "HierarchicalDesign" schematic
And the "MultiSupplierInventory" inventory
Positive and negative assertions (Axiom #6)
A scenario that tests selection logic must assert both what is selected and what
is excluded.
Complete (correct):
Then the BOM contains R1 matched to R001 with priority 0
And the BOM excludes R002 and R003
Incomplete (wrong):
Then the BOM contains R1 matched to R001
# Does not prove R002 and R003 were rejected
The "Because" test (Axiom #20)
Any impulse to write "THEN … because …", "THEN … due to …", or "THEN … based
on …" signals an incomplete Given or a vague When.
Properly structured:
Given a schematic with R1 (10K, 0603)
And inventory contains R001 (10K, 0603, priority=0) and R002 (10K, 0603, priority=1)
When I generate a BOM with priority-based selection
Then the BOM contains R1 matched to R001
And the BOM excludes R002
"Because" code smell (wrong):
Given a schematic with R1 (10K, 0603)
# Missing: inventory contents and selection algorithm
When I generate a BOM
Then the BOM contains R1 matched to R001
And the BOM excludes R002 due to higher priority values
Check: Does the Then need to explain itself? If yes, add the missing context to
Given or sharpen the When.
Named references over implicit context (Axiom #21)
When a scenario involves more than one project, schematic, or inventory, bind
each artifact to an explicit name and use that name in the When step.
Explicit (correct):
Given a KiCad project named "SimpleProject"
And an inventory named "InvalidInventory"
And the inventory contains:
| InvalidColumn | AnotherBadColumn |
| data1 | data2 |
When I generate a generic BOM with SimpleProject and InvalidInventory
Implicit (wrong):
Given a KiCad project named "SimpleProject"
And an inventory file with invalid format
When I generate a BOM with --generic fabricator
# Which project? Which inventory?
KiCad project/schematic architecture (Axiom #24)
KiCad projects contain schematics; schematics contain components. Steps must
reflect this hierarchy.
Correct hierarchy:
Given a KiCad project named "PowerSupply"
And the project uses a schematic named "MainBoard"
And the "MainBoard" schematic contains components:
| Reference | Value | Footprint | LibID |
| R1 | 10K | R_0603_1608 | Device:R |
Wrong — components placed directly in project:
Given a KiCad project named "PowerSupply" containing components:
| Reference | Value | Footprint | LibID |
| R1 | 10K | R_0603_1608 | Device:R |
# Projects don't contain components — schematics do.
For hierarchical designs, list every sub-schematic explicitly:
Given a KiCad project named "ComplexBoard"
And the project uses a schematic named "MainBoard"
And the project uses a schematic named "PowerSupply"
And the "MainBoard" schematic contains components:
| Reference | Value | Footprint |
| U1 | MCU | QFP64 |
And the "PowerSupply" schematic contains components:
| Reference | Value | Footprint |
| U2 | VREG | SOT23 |
When I generate a generic BOM for ComplexBoard using inventory.csv
Then the BOM file contains components from all schematic files
And component quantities are correctly aggregated across all schematics
Descriptive content over value judgments (Axiom #22)
Do not label test data "valid" or "invalid". Describe what it contains.
Descriptive (correct):
And an inventory file contains:
| InvalidColumn | AnotherBadColumn |
| data1 | data2 |
Value judgment (wrong):
And an inventory file with invalid format
# "Invalid" according to whom? For what fabricator?
Complete output specification (Axiom #23)
When testing data transformation, include every expected output column in the
Then table — including columns expected to be empty.
Complete (correct):
Then the BOM contains:
| Reference | Quantity | Description | Value | Package | Footprint | Manufacturer | Part Number |
| R1 | 1 | | 10k | | | | |
| C1 | 1 | | 100nF | | | | |
Partial (wrong):
Then the BOM contains components from the schematic:
| Reference | Quantity | Value |
| R1 | 1 | 10k |
| C1 | 1 | 100nF |
# What about the other columns? Are they empty? Default values?
Review checklist
Run this checklist on every scenario before committing.
Foundational (required for ALL scenarios)
Quality (essential for robustness)
Advanced (optimizing maintainability)
Precision (eliminating ambiguity)