Is there a way to use pytest.raises in pytest-bdd "When" steps?

437 Views Asked by At

I would like to define a scenario as follows:

Scenario: An erroneous operation
  Given some data
  And some more data
  When I perform an operation
  Then an exception is raised

Is there a good way to do this so that the when step isn't actually called until a pytest.raises context manager can be constructed for it? I could use a callable fixture, but that's going to pollute all other uses of the "I perform an operation" step.

1

There are 1 best solutions below

1
On

I'm not sure that I understood your question correctly, but aren't you trying to achieve something like this?

  1. Before each When step we are checking if next Then step contains "is raised".

  2. If so, we mark this When step as "expected to fail".

  3. During needed When step execution we check corresponding flag and use pytest.raises method to handle it.

For first two steps I use pytest_bdd_before_step hook and request fixture. And for 3rd I just define some function handle_step right in test module. Of course you should move it somewhere else. This function requires step (which is just some defined function with your code) and request.node.step.expect_failure to decide whether to use pytest.raises or not.

As an option you can use callable fixture (requesting request fixture) to store this function and avoid using request.node.step.expect_failure in such keywords.

Also you can add functionality to define allowed exceptions and so on.

test_exception.py

import pytest
from pytest_bdd import then, when, scenario


@scenario("exc.feature", "Test expecting correct exception")
def test_1():
    pass


def handle_step(step, expect_failure):
    if expect_failure:
        pytest.raises(Exception, step)
    else:
        step()


@when("I perform an operation")
def operation_calling_exception(request):
    def step():
        # some code that causes an exception
        print(1 / 0)
    handle_step(step, request.node.step.expect_failure)


@then('an exception is raised')
def ex_raised():
    pass

exc.feature

Feature: Target fixture
    Scenario: Test expecting correct exception
        When I perform an operation
        Then an exception is raised

conftest.py

def pytest_bdd_before_step(request, feature, scenario, step, step_func):
    # set default `expect_failure` for step
    step.expect_failure = False

    # make step available from request fixture
    request.node.step = step

    # Only for `When` keywords
    if step.keyword == "When":
        # get current step position in scenario
        step_position = scenario.steps.index(step)

        # get following `Then` step
        then_step = next((s for s in scenario.steps[step_position:] if s.keyword == "Then"), None)

        if "is raised" in then_step.name:
            step.expect_failure = True