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

Unable to fully override db setup fixture #1131

Open
eldjh3 opened this issue Jul 12, 2024 · 0 comments
Open

Unable to fully override db setup fixture #1131

eldjh3 opened this issue Jul 12, 2024 · 0 comments

Comments

@eldjh3
Copy link

eldjh3 commented Jul 12, 2024

My project uses django_audit_log. When pytest creates the test database it fails because whilst creating it and applying migrations it generates models. These trigger the auditlog, whose own latest migrations have not yet been applied, which attempts to insert an audit record. This fails due to a database schema mismatch with the audit log model.

To avoid the issue I would like to override the django_db_setup fixture, something akin to the following:

@pytest.fixture(scope="session")
def django_db_setup(django_db_setup):
    from auditlog.context import disable_auditlog
    with disable_auditlog():
        # Invoke the db_setup logic here...

The issue of course is that by the time the overriding fixture is called, the overridden one has already been executed. The examples at https://docs.pytest.org/en/7.1.x/how-to/fixtures.html#overriding-fixtures-on-various-levels show various means of overriding fixtures but they are all fairly trivial, there are no examples where the fixture does something relatively heavyweight.

I don't want to copy/paste the logic of django_db_setup and amend as that will likely lead to issues if the code changes in future.

To ease this, would you consider splitting "complex" fixtures such as this into two parts? The fixture and an implementing function, the fixture simply delegating to the function? The fixture should also be documented as simply delegating to the defined function.

This would allow users to override just the fixture, but reuse the implementation.

The implementation itself can't be a fixture as the same issue arises. If it's passed as a dependency then it's already executed by the time the overriding fixture is invoked.

One alternative is to have a delegate fixture that returns a function that invokes the original fixture, wrapping with whatever logic is desired. The issue here is that pytest checks for direct calls and rejects them. This of course can be worked around by poking inside the wrapped object, but that too is a little 'ick'.

E.g.

@pytest.fixture(scope="session")
def django_db_setup(db_setup_delegate):
    db_setup_delegate()

@pytest.fixture(scope="session")
def db_setup_delegate(request, django_test_environment,    \
    django_db_blocker, django_db_use_migrations,           \
    django_db_keepdb, django_db_createdb, django_db_modify_db_settings):

    def do_setup():
        from auditlog.context import disable_auditlog
        with disable_auditlog():
            from pytest_django.fixtures import django_db_setup as dbs
            # This fails if called on dbs directly
            next(dbs.__pytest_wrapped__.obj(
                request,
                django_test_environment,
                django_db_blocker,
                django_db_use_migrations,
                django_db_keepdb,
                django_db_createdb,
                django_db_modify_db_settings)
            )

    return do_setup

Thoughts? Is there an easy solution I've missed?

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant