pythonpytestfixturesgherkinpytest-bdd

How might I use the same step in the same scenario, but with different parameters in pytest-bdd?


Assume I have a scenario similar to this:

    Scenario Outline: Example scenario
        Given the subprocess is running
        When I generate the input
        And I add <argument1> to the input
        And I add <argument2> to the input
        And this input is passed to the subprocess
        Then the output should match the <output> for <argument1> and <argument2>

I'd very much like to reuse the 'when' step as, e.g. And I add <argument> to the input, but don't want to use an Examples table as I wish the fixtures to by dynamically generated in the step definition/conftest file. I'm currently using @pytest.mark.parametrize to parametrize the scenario outlines like so:

import pytest
from pytest_bdd import scenario
from functools import partial
from some_lib import test_data, utils

@pytest.fixture(scope='module')
def context():
    return {}

scenario = partial(scenario, '../features/example.feature')

@pytest.mark.parametrize(
    [argument1, argument2],
    [(test_data.TEST_ARGUMENT[1], test_data.TEST_ARGUMENT[2]),],
)
@scenario('Example scenario')
def test_example_scenario(context, argument1, argument2):
    pass

I would like to be able to reuse the same step definition in the same scenario with the different arguments somehow, e.g.

@when('I add <argument> to the input')
def add_argument(context, argument):
    context['input'] = utils.add_argument(context['input'], argument)

rather than having to have two step definitions, e.g.

@when('I add <argument1> to the input')
def add_argument(context, argument1):
    context['input'] = utils.add_argument(context['input'], argument1)

@when('I add <argument2> to the input')
def add_argument(context, argument2):
    context['input'] = utils.add_argument(context['input'], argument2)

The pytest-bdd documentation seems to suggest this is possible, but I can't quite wrap my head around how I might accomplish this without using example tables.

Often it’s possible to reuse steps giving them a parameter(s). This allows to have single implementation and multiple use, so less code. Also opens the possibility to use same step twice in single scenario and with different arguments! [sic] (Emphasis my own)

Does anyone have any ideas on how I might accomplish this?

Thank you for your time as always!


Solution

  • I think the pytest-bdd documentation is rather suggesting re-usage of a step due to a variable in the step definition instead of a hard-coded value...so I think the documentation does not give you any solution for your problem.

    Anyway, there is a solution that I use, which is getting the value of the step variable dynamically. Pytest-bdd will create a pytest-fixture for every variable you define in your steps and therefore you can obtain the value of a fixture by calling request.getfixturevalue(name_of_fixture), as long as you know the name of the fixture.

    For your case I would use parsers.parse() for the step definitions, so that the variables argument1 and argument2 will hold the name of the fixtures instead of their value.

    Example

    @when(parsers.parse('I add {argument1} to the input'))
    def add_argument(request, context, argument1):
        # Remove angle brackets, because they are not part of the fixture name 
        argument1 = argument1.replace('<', '').replace('>', '')
        argument_value = request.getfixturevalue(argument1)
        context['input'] = utils.add_argument(context['input'], argument_value)