Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams
@pytest.fixture(scope="module")
def dummy_article(request, db):
    return mixer.blend("core.article", title="this one price", internal_id=3000)
def test_article_str_method(dummy_article):
    assert (
        str(dummy_article)
        == f"article with ID {dummy_article.internal_id} and title: {dummy_article.title}"

I'm getting the following error:

ScopeMismatch: You tried to access the 'function' scoped fixture 'db' with a 'module' scoped request object, involved factories
core/tests/test_article_model.py:13:  def dummy_article(request, db)

The error goes away if I change the fixture to use scope="function", but that defeats the purpose of having it available to other tests and not having to "set-up" for every test.

How can I have fixtures with db access that have a scope wider than function?

The db fixture has the function scope for a reason, so the transaction rollbacks on the end of each test ensure the database is left in the same state it has when test starts. Nevertheless, you can have the session/module scoped access to database in fixture by using the django_db_blocker fixture:

@pytest.fixture(scope='module')
def get_all_models(django_db_blocker):
    with django_db_blocker.unblock():
        return MyModel.objects.all()

Warning

Beware that when unlocking the database in session scope, you're on your own if you alter the database in other fixtures or tests. In the example below I create an entity of Foo in a session-scoped fixture create_foo, then cache the queryset for session in all_foos:

# models.py
from django.db import models
class Foo(models.Model):
    name = models.CharField(max_length=16)
@pytest.fixture(scope='session', autouse=True)
def create_foo(django_db_blocker):
    with django_db_blocker.unblock():
        Foo.objects.create(name='bar')
@pytest.fixture(scope='module')
def all_foos(django_db_blocker):
    with django_db_blocker.unblock():
        yield Foo.objects.all()
def test_1(all_foos):
    assert all_foos.exists()
def test_2(all_foos, db):
    all_foos.delete()
    assert not Foo.objects.exists()
def test3(all_foos):
    assert all_foos.exists()

After the test_2 runs, the queryset stored in session from all_foos will be empty, causing test_3 to fail:

test_foo.py::test_1 PASSED                                                           [ 33%]
test_foo.py::test_2 PASSED                                                           [ 66%]
test_foo.py::test_3 FAILED                                                           [100%]
========================================= FAILURES ========================================
__________________________________________ test_3 _________________________________________
all_foos = <QuerySet []>
    def test_3(all_foos):
>       assert all_foos.exists()
E       assert False
E        +  where False = <bound method QuerySet.exists of <QuerySet []>>()
E        +    where <bound method QuerySet.exists of <QuerySet []>> = <QuerySet []>.exists
test_foo.py:28: AssertionError

Consequence: never store references in session scope if you don't want to introduce a global state that can change in tests. Query the data from database and return copies or serialized data, and so on.

Example for a safe usage:

@pytest.fixture(scope='session')
def foo_names(django_db_blocker):
    with django_db_blocker.unblock():
        names = list(Foo.objects.values_list('name', flat=True))
    return names
                This was really helpful. On my end I also had to add django_db_setup for a pytest session fixture in order to have django_db_blocker in Django 4. Not sure exactly why that was the case, and may not be the case for others. But its something to try if you get an error that the table you're trying to access doesn't exist yet
– armont_development
                Mar 30 at 17:22
        

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.