How to override a pytest fixture calling the original in pytest 4
Asked Answered
O

2

13

I am defining a pytest fixture that to overrides the django_db_setup fixture.

The change I have sets up additional teardown for safety, as there are integration tests that use this fixture which may spawn processes and cleanup is sometimes required to keep all things from breaking.

This seems reasonable, and is also suggested in pytest docs. However, I don't want to copy paste the same exact logic of django_db_setup since I'm happy with what is already there. Running it as a function, however, raises a deprecation warning:

/usr/local/lib/python3.6/dist-packages/_pytest/fixtures.py:799:

 RemovedInPytest4Warning: Fixture "django_db_setup" called directly.
 Fixtures are not meant to be called directly, are created automatically
 when test functions request them as parameters. See
 https://docs.pytest.org/en/latest/fixture.html for more information.

What would be the recommended way to deal with this situation in pytest 4? Are we encouraged to copy-paste code from fixtures we want to override, or is there another way to "inherit" a fixture, and inject e.g custom behavior before as well as after it is called?

Occupier answered 16/5, 2019 at 8:1 Comment(0)
T
13

To inject custom behavior before the initial fixture is called you can create separate fixture with this behavior and use it before the initial fixture in parameter list of fixture that overrides previously defined:

@pytest.fixture(scope='session')
def inject_before():
    print('inject_before')

@pytest.fixture(scope='session')
def django_db_setup(inject_before, django_db_setup):
    print('inject_after')
Tetrapterous answered 16/5, 2019 at 23:26 Comment(1)
Thanks, I think this is what I'm looking for. I'm going to try this out and report back, but the docs seem to support that declaration order matters: docs.pytest.org/en/latest/…Occupier
G
8

There is a simple trick to redefine a fixture with a custom impl. Just declare a fixture with the same name and signature in your local test code (I usually do it in the conftest.py in project root). Examples:

"Inheritance"

# conftest.py

import pytest


@pytest.fixture(scope='session')
def django_db_setup(
    request,
    django_db_setup,
    django_test_environment,
    django_db_blocker,
    django_db_use_migrations,
    django_db_keepdb,
    django_db_createdb,
    django_db_modify_db_settings,
):
    # do custom stuff here
    print('my custom django_db_setup executing')

Notice I have django_db_setup argument in the custom django_db_setup fixture - this ensures the original fixture is called before the custom one.

"Redeclaration"

If you omit the argument, the custom fixture will replace the original one, so it won't be executed:

@pytest.fixture(scope='session')
def django_db_setup(
    request,
    django_test_environment,
    django_db_blocker,
    django_db_use_migrations,
    django_db_keepdb,
    django_db_createdb,
    django_db_modify_db_settings,
):
    print((
        'my custom django_db_setup executing - '
        'original django_db_setup will not run at all'
    ))

BTW, this is another handy trick to use when you e.g. want to turn off a fixture that is defined elsewhere.

Giffard answered 16/5, 2019 at 19:42 Comment(2)
But how can I do actions both before and after the original django_db_setup is called?Occupier
It seems that the order of fixture arguments now influences the execution order (at least for the same scope), so the other answer seems to be correct; disregard my previous comments.Giffard

© 2022 - 2024 — McMap. All rights reserved.