Skip to content
CodeKruPro

Framework · Python

BDD with Cucumber or behave

Feature files, step definitions and when BDD helps.

Learn: BDD in Python with pytest-bdd and behave9 minute read

BDD (behaviour-driven development) is a way of working where the team agrees on examples of how the product should behave, then turns those examples into tests that run. The examples are written in Gherkin, a plain-language format with Given, When and Then steps. Python has two well-known BDD tools. pytest-bdd runs each scenario as an ordinary pytest test, so fixtures, markers and plugins keep working. behave is a separate runner with its own hooks and a shared context object. Interviewers ask how the parts fit, how data reaches the steps, and whether BDD is worth it.

How the parts fit

The runner reads the feature file and finds step definitions; each step matches a step definition that calls page objects, which drive the browser, while hooks open and quit the browser.
Feature files describe behaviour, step functions turn steps into code, and page objects do the clicking.

Feature files

A feature file is a text file ending in .feature that holds scenarios, which are the examples. Each scenario is a list of steps:

@checkout
Feature: Checkout

  Background:
    Given I am logged in as a customer

  @smoke
  Scenario: Pay with a saved card
    Given my cart holds 2 items
    When I pay with my saved card
    Then I see the order confirmation

  Scenario Outline: Coupon rules
    Given my cart total is <total>
    When I apply the coupon "<code>"
    Then I see the message "<message>"

    Examples:
      | total | code   | message         |
      | 100   | SAVE10 | Coupon applied  |
      | 20    | SAVE10 | Minimum not met |

A Scenario is one fixed example. A Scenario Outline runs once per Examples row, filling the placeholders in angle brackets. A Background runs before every scenario in the file and should stay short.

pytest-bdd

A step function is a Python function that runs when a step's text matches its pattern.

from pytest_bdd import scenarios, given, when, then, parsers

scenarios("features/checkout.feature")

@given(parsers.parse("my cart holds {count:d} items"), target_fixture="cart")
def cart(api, count):
    return api.new_cart(items=count)

@when(parsers.parse('I apply the coupon "{code}"'))
def apply_coupon(cart_page, code):
    cart_page.apply_coupon(code)

@then(parsers.parse('I see the message "{message}"'))
def see_message(cart_page, message):
    assert cart_page.message() == message
  • scenarios(...) turns every scenario in the file into a pytest test; @scenario binds a single one.
  • Step functions ask for fixtures like any test: driver, page objects, an API client.
  • Parsers pull values out of the step text, for example the number 2 from "my cart holds 2 items". parsers.parse is the readable one, with types such as :d for whole numbers. parsers.cfparse adds repeated fields, and parsers.re uses regular expressions (patterns for matching text) with named groups.
  • target_fixture makes a step's return value available to later steps as a fixture. That is how steps share data without global variables.
  • Common steps in conftest.py are shared by every feature file below it.

Tags (labels such as @smoke on a scenario) become pytest markers with the @ removed, so pytest -m "smoke and not wip" selects scenarios; register the tags as markers. Set-up and teardown are normal fixtures, and screenshots on failure use the same report hook as any pytest test (see Reports, screenshots and logs). Because scenarios are pytest tests, pytest-xdist, pytest-html and Allure all work.

behave

behave expects a folder layout: feature files in features/, step modules in features/steps/, and hooks in features/environment.py.

# features/environment.py
from selenium import webdriver

def before_scenario(context, scenario):
    context.driver = webdriver.Chrome()

def after_step(context, step):
    if step.status == "failed":
        context.driver.save_screenshot(f"{step.name[:40]}.png")

def after_scenario(context, scenario):
    context.driver.quit()

Steps use from behave import given, when, then and receive context, which carries values between the steps of a scenario (a plain object where you can store things, such as context.driver). The default step matcher uses the same {name:d} style as parse. A table under a step is context.table, read row by row with row["name"], and multi-line text is context.text. Tags are chosen with behave --tags=smoke or --tags="not slow".

A team that already uses pytest usually picks pytest-bdd, because fixtures, markers, parallel runs and report plugins carry over. behave suits teams that want a BDD-first runner with a simple folder convention.

How to answer in an interview

Walk the picture: feature files, step functions matched by parsers, page objects underneath, fixtures or environment.py hooks for the browser, and tags to pick what runs. Then say who reads the feature files. BDD earns its cost when business people (such as product owners, who decide what the product should do) read or write them; when only testers do, Gherkin is an extra layer to maintain.

Common mistakes

  • Click-by-click scenarios instead of declarative steps (they say what the user wants, not each click) such as "When I pay with my saved card".
  • Passing data between steps through globals instead of target_fixture or context.
  • Writing pytest --tags, which is behave's option, not pytest's.
  • Near-duplicate steps with slightly different wording.

Recap

Gherkin feature files describe behaviour. In pytest-bdd, scenarios() turns them into pytest tests, parsers feed step arguments, and target_fixture shares values. In behave, environment.py holds the hooks and context carries values. Either way, steps stay thin and call page objects (see Page objects and design patterns).

9 questions0/9 solved

StatusQuestionDifficultyAsked in
Not startedScenario versus Scenario OutlineEasyAsked in: Accenture, Capgemini, Infosys +1
Not startedBDD in Python: feature files and step definitions with pytest-bddMediumAsked in: Accenture, Capgemini, Coforge +3
Pro+Scenario Outline, Examples and BackgroundMediumAsked in: Accenture, Capgemini, Infosys +1
Pro+Hooks and tags in Python BDD toolsMediumAsked in: Accenture, Capgemini, Infosys +1
Pro+Parameters, outlines and tables in Python BDD stepsMedium-HardAsked in: Accenture, Capgemini, Infosys +2
Pro+Run only @smoke scenarios with pytest-bddEasy-MediumAsked in: Accenture, Capgemini, Infosys +1
Pro+Tag expressionsEasy-MediumAsked in: Accenture, Capgemini, Infosys +1
Pro+When does BDD help, and when is it overhead?MediumAsked in: Accenture, Capgemini, Coforge +3
Pro+The layout of a behave projectEasy-MediumAsked in: Accenture, Capgemini, Coforge +3
Not startedNot startedIn progressIn progressSolvedSolvedPro+Pro+

The first 2 questions of each topic are free. Pro+ opens every question.

Back to the plan