pytest-bdd

repository·master·Indexed 23 days ago

https://github.com/pytest-dev/pytest-bdd

A Behavior-Driven Development (BDD) framework for pytest that implements a subset of the Gherkin language. It enables developers to write requirements in human-readable feature files and map them to pytest fixtures and functions, allowing for the unification of unit and functional tests through direct integration with pytest's dependency injection and fixture system.

Tokens
11.7K
Snippets
28
Records
61
Agent score
80%

What's inside pytest-bdd

  1. Reuse steps from conftest.py

    master

    Step definitions (using @given, @when, @then) defined in a parent conftest.py are automatically available to all test files in that directory and its subdirectories. This allows you to define common domain steps once and use them across multiple feature files without re-defining them in every test module.

    # In conftest.py
    @given("I have a bar", target_fixture="bar")
    def bar():
        return "bar"
    
    # In test_common.py
    @scenario("common_steps.feature", "All steps are declared in the conftest")
    def test_conftest():
        pass
  2. Use Scenario Outlines for parametrization

    master

    Gherkin Scenario Outlines allow you to run the same scenario multiple times with different data using Examples tables. Variable templates in the feature file use angular brackets (e.g., <var_name>).

    In your Python code, use parsers.parse to map these variables to step arguments. You can also use multiple Examples blocks in a single scenario outline, which can be tagged to allow filtering during execution (e.g., pytest -k "tag_name").

    Feature: Scenario outlines
        Scenario Outline: Outlined given, when, then
            Given there are <start> cucumbers
            When I eat <eat> cucumbers
            Then I should have <left> cucumbers
    
            Examples:
            | start | eat | left |
            | 12    | 5   | 7    |
  3. How pytest-bdd works with pytest

    master

    pytest-bdd implements a subset of the Gherkin language to automate requirement testing. Unlike standalone BDD runners, it integrates directly with pytest, allowing you to:

    • Unify unit and functional tests.
    • Reuse existing pytest fixtures for BDD step setups via dependency injection.
    • Avoid maintaining a separate context object for side effects by using standard pytest fixtures.

    Each feature file should contain only one Feature definition.

  4. Programmatically generate step definitions using stacklevel

    master

    To automate the creation of step definitions (e.g., when using factory libraries like pytest-factoryboy), use the stacklevel parameter in @given, @when, @then, or @step.

    Setting stacklevel tells the decorator to inject the step fixtures into the module where the generator is called, rather than the caller's frame. This is essential when generating steps inside a helper function that is then imported into a test file.

  5. Use Gherkin Backgrounds for common setup

    master

    A Background section in a Gherkin feature allows you to define a set of steps that run before every scenario in that feature. This is ideal for putting the system into a known state.

    Constraint: Only Given steps should be used in a Background. Using When or Then is prohibited because backgrounds are intended for setup, not for describing actions or outcomes.

    Feature: Multiple site support
    
      Background:
        Given a global administrator named "Greg"
        And a blog named "Greg's anti-tax rants"
    
      Scenario: Wilson posts to his own blog
        Given I am logged in as Wilson
        # ...
  6. Use Gherkin Rules to group scenarios

    master

    Gherkin Rules allow you to group related scenarios or examples under a shared context.

    • Scenario and Example are aliases and function identically within a rule.
    • Tags applied to a Rule are automatically inherited by all Examples or Scenarios defined under that rule. This is useful for organizing and filtering tests via pytest -k.
    Feature: Rules and examples
    
        @feature_tag
        Rule: A rule for valid cases
    
            @rule_tag
            Example: Valid case 1
                Given I have a valid input
                When I process the input
                Then the result should be successful
    
        Rule: A rule for invalid cases
    
            Example: Invalid case
                Given I have an invalid input
                When I process the input
                Then the result should be an error
  7. Manage test setup using `target_fixture` and Pytest fixtures

    master

    pytest-bdd uses Pytest's dependency injection for test setup. Instead of a global context, you define setup in Given steps and use target_fixture to make the return value available to subsequent steps.

    Using target_fixture

    When a @given step uses target_fixture="name", the return value of that function becomes a Pytest fixture named name.

    Reusing Fixture Libraries

    You can combine standard @pytest.fixture definitions with @given steps to apply side effects to existing objects. Fixtures are evaluated only once within the Pytest scope and their values are cached.

    @pytest.fixture
    def article():
        return Article(is_beautiful=True)
    
    @given("my article is published")
    def published_article(article):
        article.publish()
        return article
    from pytest_bdd import given, when, then
    
    # 1. Define setup and export as a fixture
    @given("I have a beautiful article", target_fixture="article")
    def article_setup():
        return Article(is_beautiful=True)
    
    # 2. Subsequent steps consume the fixture by name
    @when("I publish this article")
    def publish_article(article):
        article.publish()
  8. Use asterisks (*) as shorthand for Gherkin keywords

    master

    To reduce redundancy in Gherkin scenarios, you can use an asterisk (*) instead of repeating keywords like And or But. The asterisk acts as a wildcard that follows the context of the previous keyword (Given, When, or Then).

    Feature: Resource owner
        Scenario: I'm the author
            Given I'm an author
            * I have an article
            * I have a pen
  9. Migrate from pytest-bdd 4.x.x to newer versions

    master

    When migrating from version 4.x.x, you must update how templated steps and converters are handled:

    • Use parsers for templated steps: Replace the <parameter> syntax with parsers.parse("{parameter}"). While parameters are still provided as fixtures, they must be explicitly parsed to match Scenario Outlines.
    • Move converters to the step level: The example_converters argument on the @scenario decorator has been removed. Instead, pass the converters dictionary directly to the step decorator (e.g., @given).
    # Old step definition:
    @given("there are <start> cucumbers")
    def given_cucumbers(start):
        pass
    
    
    # New step definition:
    @given(parsers.parse("there are {start} cucumbers"))
    def given_cucumbers(start):
        pass
    
    
    # Old code (converters on scenario):
    @given("there are <start> cucumbers")
    def given_cucumbers(start):
        return {"start": start}
    
    @scenario("outline.feature", "Outlined", example_converters={"start": float})
    def test_outline():
        pass
    
    
    # New code (converters on step):
    @given(parsers.parse("there are {start} cucumbers"), converters={"start": float})
    def given_cucumbers(start):
        return {"start": start}
    
    @scenario("outline.feature", "Outlined")
    def test_outline():
        pass
  10. Organize and filter scenarios using Gherkin tags

    master

    pytest-bdd supports Gherkin tags (e.g., @login, @backend) by converting them into standard pytest markers. This allows you to use pytest's test selection capabilities to run specific subsets of your BDD scenarios.

    1. In Gherkin: Use @tag_name above Features or Scenarios.
    2. In CLI: Use pytest -m "tag_name".

    Best Practices:

    • Use names that are Python-compatible variable names (start with a non-number, use alphanumeric/underscores).
    • If using --strict-markers, register your tags in pytest.ini.
    • To avoid collisions with other test markers, consider using a prefix like bdd_ (e.g., @bdd_login).
    @login @backend
    Feature: Login
    
      @successful
      Scenario: Successful login
    # Run only scenarios tagged with @backend, @login, and @successful
    pytest -m "backend and login and successful"
  11. Generate missing test code and step definitions

    master

    pytest-bdd includes a smart code generation feature that identifies scenarios not yet bound to tests or steps that lack implementations. It validates files for format errors and logic bugs (like step ordering) as a side effect. You can invoke this tool by passing the --generate-missing flag to pytest.

    When run, the tool outputs the suggested Python code (using @scenario and step decorators like @given) which you can then copy into your test files.

    pytest --generate-missing --feature features tests/functional