Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to reuse same fixture implementation with different scopes? #3425

Closed
jurisbu opened this issue Apr 24, 2018 · 9 comments
Closed

How to reuse same fixture implementation with different scopes? #3425

jurisbu opened this issue Apr 24, 2018 · 9 comments
Labels
topic: fixtures anything involving fixtures directly or indirectly type: question general question, might be closed after 2 weeks of inactivity

Comments

@jurisbu
Copy link

jurisbu commented Apr 24, 2018

Hello!

I am looking for a way to reuse same fixture or same fixture implementation in different scopes. I would like to avoid copy-pasting implementation in multiple fixtures with different scopes defined. What would be recommended approach?

So far I have thought about (ab)using yield from as in following example. Not sure if this can lead to some unexpected issues.

@pytest.fixture(session='function')
def my_fixture_fun():
    yield from fixture_impl()

@pytest.fixture(scope='module')
def my_fixture_mod():
    yield from fixture_impl()

@pytest.fixture(scope='session')
def my_fixture_ses():
    yield from fixture_impl()

def fixture_impl():
    # Rather long on complicated fixture implementation here
    print('SETUP: Fixture implmentation')
    yield
    print('TEARDOWN: Fixture implmentation')

Any other ideas or suggestions?
Thanks!

@pytestbot pytestbot added the topic: fixtures anything involving fixtures directly or indirectly label Apr 24, 2018
@pytestbot
Copy link
Contributor

GitMate.io thinks possibly related issues are #538 (Fixture scope documentation), #1552 (Suggestion: add new 'parametrization' fixture scope), #2732 (Fixture scope mixing?), #3393 (Customize the fixture ordering on the same scope level), and #668 (autouse fixtures break scope rules).

@RonnyPfannschmidt
Copy link
Member

iriginally multi scoped fixtures was planned for pytest 3.0, but it demonstrated impossible to implement without a major refactoring, what you do there is the common workaround, but its suggested to use a contextmanager instead of yield from

@RonnyPfannschmidt RonnyPfannschmidt added the type: question general question, might be closed after 2 weeks of inactivity label Apr 24, 2018
@jurisbu
Copy link
Author

jurisbu commented Apr 24, 2018

@RonnyPfannschmidt Thanks for such a prompt response. As per your suggestions of using contexmanager instead, do you mean something along these lines?

@pytest.fixture(scope='module')
def my_fixture_mod():
    with fixture_impl():
        pass


@contextmanager
def fixture_impl():
    print('BEFORE: Fixture implmentation')
    yield
    print('AFTER: Fixture implmentation')

@RonnyPfannschmidt
Copy link
Member

@jurisbu correct

with fixture_impl(...) as result:
   yield result

@jurisbu
Copy link
Author

jurisbu commented Apr 24, 2018

Satisfactory answer received. Closing.

@jurisbu jurisbu closed this as completed Apr 24, 2018
@iAnanich
Copy link

iAnanich commented Aug 6, 2021

Same idea can be applied to async fixtures/tests:

@pytest.fixture(scope='module')
async def my_fixture_mod():
    async with fixture_impl() as result:
        yield result


@asynccontextmanager
async def fixture_impl():
    print('BEFORE: Fixture implementation')
    yield
    print('AFTER: Fixture implementation')

@oleg-kondaurov
Copy link

oleg-kondaurov commented Oct 13, 2021

@RonnyPfannschmidt @jurisbu @iAnanich Is it possible to somehow trigger the test state to become error or fail in case if an exception is raised in setup or teardown block of the contextmanager?

Current example causes the unhandled exception and pytest process close:

@pytest.fixture(scope='module')
def my_fixture_mod():
    with fixture_impl(...) as result:
       yield result

@contextmanager
def fixture_impl():
    print('raise exception in BEFORE...')
    print(1/0)
    yield
    print('raise exception in AFTER...')
    print(1/0)

@dmos62
Copy link

dmos62 commented May 19, 2022

I've been doing this:

def _some_fixture(a_dependency_fixture):
    def __some_fixture(x):
        return x
    yield __some_fixture

some_temp_fixture = pytest.fixture(_some_fixture, scope="function")
some_module_fixture = pytest.fixture(_some_fixture, scope="module")
some_session_fixture = pytest.fixture(_some_fixture, scope="session")

Seemed more straightforward than a context manager. Any downsides to this?

@nicoddemus
Copy link
Member

Any downsides to this?

Currently we add an attribute to the function decorated by @pytest.fixture:

function._pytestfixturefunction = self # type: ignore[attr-defined]

So the fixtures above will actually point to the wrong definition... I'm surprised it works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: fixtures anything involving fixtures directly or indirectly type: question general question, might be closed after 2 weeks of inactivity
Projects
None yet
Development

No branches or pull requests

7 participants