Table of Contents
- What is Behavior-Driven Development (BDD)?
- Core BDD Concepts
- Gherkin: The Language of BDD
- Features, Scenarios, and Steps
- Getting Started with Behave
- Installation
- Project Structure
- Writing Your First Feature File
- Implementing Step Definitions
- Running Tests and Interpreting Results
- Advanced Behave Features
- Background
- Scenario Outlines (Data-Driven Testing)
- Tags
- Hooks
- Best Practices for BDD with Behave
- Conclusion
- References
1. What is Behavior-Driven Development (BDD)?
BDD evolved from Test-Driven Development (TDD) but shifts the focus from “testing” to “behavior.” It encourages teams to define expected behavior of software through conversations between technical (developers, testers) and non-technical (product owners, users) stakeholders. These conversations result in “living documentation”—testable scenarios that double as requirements.
Key goals of BDD:
- Align development with business goals.
- Improve communication and collaboration.
- Create maintainable, readable tests that serve as documentation.
- Validate software behavior from the user’s perspective.
2. Core BDD Concepts
Gherkin: The Language of BDD
Gherkin is a plain-text language used to write BDD scenarios. It’s human-readable, making it accessible to non-technical stakeholders, and structured, allowing tools like Behave to automate the scenarios. Gherkin uses keywords (in English or 70+ languages) to define behavior.
Common Gherkin Keywords:
Feature: A high-level description of a software feature (e.g., “User Authentication”).Scenario: A specific test case for the feature (e.g., “Successful login with valid credentials”).Given: Sets up the initial context (e.g., “a user with username ‘test’ and password ‘pass’”).When: Describes the action taken (e.g., “the user enters their username and password”).Then: Defines the expected outcome (e.g., “the user is redirected to the dashboard”).And/But: ExtendsGiven,When, orThenfor readability (e.g., “And clicks ‘Login’”).Scenario Outline: Reuses a scenario with multiple data sets (data-driven testing).Examples: Provides data for aScenario Outline(in a table format).
Features, Scenarios, and Steps
- Feature File: A
.featurefile containing one or more scenarios for a feature. Stored in afeatures/directory (Behave’s default). - Scenario: A sequence of
Given-When-Thensteps that describe a specific behavior. - Step: A single action or assertion in a scenario (e.g.,
Given I have 5 apples).
3. Getting Started with Behave
Installation
Behave is a Python package. Install it via pip:
pip install behave
Project Structure
Behave follows a standard directory structure. For a project named my_project, organize files like this:
my_project/
├── features/ # Root directory for BDD tests
│ ├── calculator.feature # Feature file(s)
│ └── steps/ # Step definitions directory
│ └── calculator_steps.py # Python code mapping Gherkin steps to logic
└── behave.ini # (Optional) Configuration for Behave
features/: Stores all.featurefiles.features/steps/: Stores Python files with step definitions (matching Gherkin steps to code).
4. Writing Your First Feature File
Let’s create a simple feature for a calculator app: adding two numbers.
Create features/calculator.feature:
Feature: Add Two Numbers
As a user
I want to add two numbers
So that I can get their sum
Scenario: Add two positive numbers
Given I have entered 5 into the calculator
And I have entered 3 into the calculator
When I press add
Then the result should be 8 on the screen
Breakdown:
Feature: Describes the purpose of the feature (who, what, why).Scenario: Tests adding two positive numbers.- Steps:
Giveninitializes the calculator with 5 and 3,Whentriggers addition,Thenchecks the result.
5. Implementing Step Definitions
Step definitions are Python functions that map Gherkin steps to executable code. They use Behave’s decorators (@given, @when, @then) to link to Gherkin keywords.
Writing Step Definitions
Create features/steps/calculator_steps.py:
from behave import given, when, then
from calculator import Calculator # Assume we have a Calculator class
@given("I have entered {number:d} into the calculator")
def step_impl(context, number):
# Initialize calculator if not exists in context
if not hasattr(context, "calculator"):
context.calculator = Calculator()
context.calculator.enter_number(number)
@when("I press add")
def step_impl(context):
context.result = context.calculator.add()
@then("the result should be {expected:d} on the screen")
def step_impl(context, expected):
assert context.result == expected, f"Expected {expected}, got {context.result}"
Key Notes:
context: A special object passed between steps to share data (e.g.,context.calculator,context.result).{number:d}: A parameter captured from the Gherkin step (here, an integerd). Behave uses regex or theparselibrary to extract parameters.CalculatorClass: We’ll need a simple implementation for this to work. Createcalculator.pyin the project root:class Calculator: def __init__(self): self.numbers = [] def enter_number(self, num): self.numbers.append(num) def add(self): return sum(self.numbers)
6. Running Tests and Interpreting Results
Run Behave
From the project root, run:
behave
Sample Output
Feature: Add Two Numbers # features/calculator.feature
As a user
I want to add two numbers
So that I can get their sum
Scenario: Add two positive numbers # features/calculator.feature:4
Given I have entered 5 into the calculator # features/steps/calculator_steps.py:5
And I have entered 3 into the calculator # features/steps/calculator_steps.py:5
When I press add # features/steps/calculator_steps.py:12
Then the result should be 8 on the screen # features/steps/calculator_steps.py:16
1 feature passed, 0 failed, 0 skipped
1 scenario passed, 0 failed, 0 skipped
4 steps passed, 0 failed, 0 skipped, 0 undefined
Failed Test Example
If we modify the Then step to expect 9 instead of 8, Behave will show a failure:
Then the result should be 9 on the screen # features/steps/calculator_steps.py:16
Assertion Error: Expected 9, got 8
7. Advanced Behave Features
Background
Reduce repetition across scenarios with Background. It runs before every scenario in a feature file.
Example:
Feature: Add Two Numbers
Background:
Given a calculator is initialized
Scenario: Add two positive numbers
Given I have entered 5 into the calculator
And I have entered 3 into the calculator
When I press add
Then the result should be 8 on the screen
Scenario: Add positive and negative numbers
Given I have entered 5 into the calculator
And I have entered -3 into the calculator
When I press add
Then the result should be 2 on the screen
Add a step definition for Given a calculator is initialized:
@given("a calculator is initialized")
def step_impl(context):
context.calculator = Calculator()
Scenario Outline (Data-Driven Testing)
Test multiple input combinations with Scenario Outline and Examples.
Example:
Scenario Outline: Add two numbers
Given I have entered <num1> into the calculator
And I have entered <num2> into the calculator
When I press add
Then the result should be <expected> on the screen
Examples:
| num1 | num2 | expected |
| 5 | 3 | 8 |
| -2 | 4 | 2 |
| 0 | 0 | 0 |
Behave runs the scenario once for each row in Examples.
Tags
Tag scenarios or features to run specific tests (e.g., @smoke, @regression).
Example:
@smoke
Scenario: Add two positive numbers
...
@regression
Scenario Outline: Add two numbers
...
Run tagged scenarios:
behave --tags=@smoke # Run only @smoke scenarios
behave --tags=@smoke,@regression # Run @smoke OR @regression
Hooks
Hooks are Python functions that run before/after scenarios, features, or steps (e.g., setup/teardown). Define them in features/environment.py:
def before_scenario(context, scenario):
context.calculator = Calculator() # Reinitialize calculator before each scenario
def after_scenario(context, scenario):
del context.calculator # Clean up after each scenario
Common hooks: before_all, after_all, before_feature, after_feature, before_step, after_step.
8. Best Practices for BDD with Behave
- Keep Scenarios Focused: Each scenario should test one behavior. Avoid “kitchen sink” scenarios.
- Use Declarative Steps: Describe what, not how. Prefer “Then the user is logged in” over “Then the ‘Welcome’ div is visible”.
- Collaborate on Scenarios: Write feature files with product owners and testers to ensure alignment.
- Reuse Steps: Avoid duplicating steps. Use
And/Butor refactor into shared steps. - Keep Data in Examples: Use
Scenario Outlinefor data-driven testing instead of hardcoding values. - Avoid UI Details: Focus on behavior, not UI elements (e.g., “clicks the button” vs. “presses Enter”).
- Review Regularly: Treat feature files as living documentation—update them as requirements change.
9. Conclusion
Behavior-Driven Development with Behave transforms how teams collaborate and validate software behavior. By writing human-readable scenarios in Gherkin and automating them with Python, you bridge the gap between technical and non-technical stakeholders, create maintainable tests, and ensure your software meets user needs.
Start small: pick a critical feature, collaborate on scenarios, and iterate. Over time, BDD will become a cornerstone of your development process, leading to more reliable software and happier teams.
10. References
- Behave Official Documentation
- Gherkin Language Guide
- BDD 101 (Cucumber)
- Book: “The Cucumber Book” by Matt Wynne & Aslak Hellesøy
- Book: “Behavior-Driven Development with Python” by Ben Bangert
This blog provides a comprehensive guide to BDD with Behave. Experiment with the examples, explore advanced features, and integrate BDD into your workflow to build better software! 🚀