pytest-dev / pytest-bdd

BDD library for the py.test runner
https://pytest-bdd.readthedocs.io/en/latest/
MIT License
1.3k stars 219 forks source link

Add built-in fixture to store data to be passed between steps #301

Open teetimeforyou opened 5 years ago

teetimeforyou commented 5 years ago

Problem Then in our integration tests given or when step do some stuff that return data needed in the next step

for example given add some to database and return id when use id for create msg to microservice and then take response then have a lot of asserts for response from when

for share data between steps we need to add something like this:

@pytest.fixture(scope='session')
def context():
    """Context object to store data to be passed between steps"""
    return Context()

This work around can be added in pytest-bdd for better support or perhaps where are another way out from this problem?

Vic152 commented 5 years ago

Did you try using pytest:

  1. I do not know if this would work but you could return in When and inject when into Then
  2. You certainly can return ID from Given step like this
    @given('I have an article')
    def article(author):
    return create_test_article(author=author)

    https://pytest-bdd.readthedocs.io/en/latest/#example

In your case it would be

@given('database id')
def database_id(request)
    response = send_request(request)
    return extract_id(response)

To hold data from When step you could have @pytest.fixture

@pytest.fixture()
def size_ranges():
    return ''

And in When populate empty string.

https://pytest-bdd.readthedocs.io/en/latest/#step-arguments-are-fixtures-as-well

  1. cache https://docs.pytest.org/en/3.0.1/cache.html
  2. tempdir https://docs.pytest.org/en/3.0.1/tmpdir.html
TBBle commented 2 years ago

In current pytest-bdd versions, I think target_fixture is what you would use here, as from 4.0, Given is no longer automatically a fixture, and from 4.1.0, When and Then also support target_fixture.

i.e. for something like

given add some to database and return id
when use id for create msg to microservice and then take response
then have a lot of asserts for response from when

You'd do something like

@given("add some to database and return id", target_fixture = db_id)
def add_some_to_database_and_return_id(db_fixture):
    id = db_fixture.add("some")
    return id

@when("use id for create msg to microservice and then take response", target_fixture=microservice_response)
def use_id_for_create_msg_to_microservice_and_then_take_response(microservice_fixture, db_id):
    response = microservice_fixture.msg(db_id)
    return response

@then("then have a lot of asserts for response from when")
def then_have_a_lot_of_asserts_for_response_from_when(microservice_response):
    assert microservice_response.lots()

For situations where you want to modify the value of the data in steps, steps can rely on a fixture, and then replace the value of fixture using target_fixture for the same value.

Slightly more messily, a Given step can produce a dictionary as a target fixture that other steps rely on and modify, which is more-like the context idea from the original issue report, except explicit in your tests, and not session-scoped so you don't get data leaking between tests.

You can of course simply have that context fixture (but probably not session-scoped) in your conftest.py, but return a dictionary, if you want to stick arbitary data in the context, although then your steps need to handle context that doesn't contain the data you need.

Anyway, I don't see anything that's worth adding directly into pytest-bdd for these use-cases, as I think target_fixture means it's possible for pytest-bdd users to solve this in a way that best-suits their codebase.