Skillversitas

Step Functions and the Context Object

Every step function receive one argument: context. It is behave's shared, per-scenario state container and the primary channel through which steps communicate. Mastering what lives in context, what its lifetime is, and what behaves adds to it is essential for writing steps that cooperate instead of fighting each other.

Lifecycle and Isolation

Behave creates a fresh context object for each scenario and discards it afterwards. Steps in the same scenario see the same attributes; steps in the next scenario start with a clean slate. This isolation guarantees scenarios stay independent — but it also means anything cached in context must be rebuilt per scenario, or hoisted into before_all fixtures stored via context.config.userdata or module-level caches.

@given("a logged-in shopper")
def logged_in(context):
    context.browser = browser_login("[email protected]", "secret")
    context.cart = []

Attributes Behave Prepopulates

Besides your own attributes, behave puts framework data on context before your steps run:

AttributeTypeMeaning
context.featureFeatureThe currently running feature: name, tags, description.
context.scenarioScenarioCurrent scenario, including outline name and tags.
context.scenario.outlineScenarioOutlineSet when the scenario is an expanded outline.
context.ruleRule or NoneEnclosing rule, if the feature uses Rule.
context.stepStepThe step currently executing; its keywords and text.
context.tableTable or NoneRows attached to the step, if any.
context.textText or NoneDocstring attached to the step, if any.
context.configConfigurationParsed options; context.config.userdata holds -D values.
context.active_stepslistStack of the currently executing steps (useful in hooks).

Reading and Writing Attributes

Assign any attribute name that suits your domain. Access before assignment raises AttributeError, which behave reports as a step failure — this is a feature: it surfaces store/state bugs in tests instead of silently passing on stale data.

@when("signup is attempted with the details")   # flask view handler smoke
def signup_attempt(context):
    payload = {"email": context.user_email,   # set by a previous Given
               "password": context.user_password}
    context.last_error = None
    context.request = call_api("POST", "/signup", payload)

Passing Values Between Step Kinds

Typical pattern: Given creates domain objects, When triggers an operation, Then asserts. All three use context keys agreed by naming convention, e.g., context.response, context.order, context.created_id:

# cart_steps.py
@given("the customer has a current cart")
def get_cart(context):
    customer = build_customer()
    context.cart = create_cart_for(customer)

@when("the item is added to the cart")
def add_item(context):
    context.cart.update({"item": context.item})    # context.item set earlier

@then("the cart shows the item")
def show(context):
    assert context.item in context.cart

Sharing Across Step Modules

The context object is one instance even when steps live in different Python files — cart_steps.py can read what auth_steps.py stored. Keep keys top-level and documented to avoid confusion. Two related notes on access logic:

  • Use getattr(context, "key", default) to read a possibly-unset value without raising.
  • Never mutate attributes behave created for you (e.g., swapping context.table) from a normal step.
  • context is reentrant: inside after_scenario you get a mostly-pristine copy per behave's fixture semantics, so don't rely on scenario-created state unless you save it yourself.

Anti-Patterns

  • Storing one big blob (a dict per scenario you poke keys into) — prefer named attributes.
  • Putting functions or lambdas on context instead of importing helpers.
  • Depending on attribute values from a previous scenario — isolation removes them by design.

Summary

The context object is per-scenario shared state: step functions read and write attributes on it, behave prepopulates framework attributes like feature, scenario, step, table, and config, and nothing survives into the next scenario.