Search code examples
pythonfixturespython-behave

Python Behave - how to pass value from a scenario to use in a fixture on a feature level?


I have the following test scenario:

  1. Check if project with a specific name was created
  2. Edit this project
  3. Verify that it was edited
  4. remove this project as part of a teardown procedure

Here is an example code to achieve that: Scenario:

  @fixture.remove_edited_project
  @web
  Scenario: Edit a project data
    Given Project was created with the following parameters
      | project_name             |
      | my_project_to_edit       |
    When I edit the "my_project_to_edit" project
    Then Project is edited

Step to save the data in some variable to be used in a teardown function(fixture):

@step('I edit the "{project_name}" project')
def step_impl(context, project_name):
    # steps related to editing the project

    # storing value in context variable to be used in fixture
    context.edited_project_name = project_name

and an example fixture function to remove a project after scenario:

@fixture
def remove_edited_project(context):
    yield
    logging.info(f'Removing project: "{context.edited_project_name}"')

    # Part deleting a project with name stored in context.edited_project_name

In such a configuration everything works fine and project is deleted by a fixture in any case(test failed or passed). Which is alright.

But, when I want to execute such a feature on a Feature level, means placing @fixture.remove_edited_project decorator before Feature Keyword:

@fixture.remove_edited_project
Feature: My project Edit feature

, then this is not working. I know the reason already - the context.edited_project_name variable is cleaned after every scenario and it's no longer available for this fixture function later.

Is there any good way in passing a parameter somehow to a fixture on a feature level? Somehow globally? I was trying to use global variables as an option, but this started to be a bit dirty and problematic in this framework.

Ideally it would be to have something like @fixture.edited_project_name('my_project_to_edit')


Solution

  • Because the context gets cleaned of variables created during execution of the scenario you need a mechanism that persists through the feature. One way to do this would be to create a dictionary or other container in the context during setup of the fixture so that it will persist through the feature. The scenarios can set attributes or add to the container and because the dictionary was added during the feature, it will still exist during destruction of the fixture. E.g.,

    @fixture
    def remove_edited_project(context):
        context.my_fixture_properties = {}
        yield
        logging.info(f'Removing project: "{context.my_fixture_properties['edited_project_name']}"')
    
    @step('I edit the "{project_name}" project')
    def step_impl(context, project_name):
        # steps related to editing the project
    
        # storing value in context variable to be used in fixture
        context.my_fixture_properties['edited_project_name'] = project_name