Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override a pytest fixture calling the original in pytest 4

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?

like image 550
tutuDajuju Avatar asked May 16 '19 08:05

tutuDajuju


People also ask

How to access fixture function in pytest?

To access the fixture function, the tests have to mention the fixture name as input parameter. Pytest while the test is getting executed, will see the fixture name as input parameter.

How does pytest handle teardown codes?

Any teardown code for that fixture is placed after the yield. Once pytest figures out a linear order for the fixtures, it will run each one up until it returns or yields, and then move on to the next fixture in the list to do the same thing.

How to manage everything in pytest?

Everything is managed by the pytest fixture system. Each method only has to request the fixtures that it actually needs without worrying about order. This is because the act fixture is an autouse fixture, and it made sure all the other fixtures executed before it.

How does pytest work in reverse?

Once the test is finished, pytest will go back down the list of fixtures, but in the reverse order, taking each one that yielded, and running the code inside it that was after the yield statement. Let’s say we want to test sending email from one user to another.


2 Answers

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')
like image 106
sanyassh Avatar answered Nov 14 '22 23:11

sanyassh


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.

like image 36
hoefling Avatar answered Nov 14 '22 22:11

hoefling