2

With the following fixture and tests, the tests pass: from deals.models import Deal

@pytest.fixture(scope='function')
def myfixture(django_db_blocker):
    with django_db_blocker.unblock():
        new_deal = Deal()
        new_deal.name = 'Alice'
        new_deal.save()

@pytest.mark.django_db
def test_1(myfixture):
    print(myfixture)
    deals = Deal.objects.all()
    assert deals

@pytest.mark.django_db
def test_2(myfixture):
    print(myfixture)
    deals = Deal.objects.all()
    assert deals

Result:

============ test session starts =============
myapp/tests/pytest_test.py::test_1 PASSED
myapp/tests/pytest_test.py::test_2 PASSED

But if I change the scope to 'module' the second one fail:

@pytest.fixture(scope='module')
def myfixture(django_db_blocker):
    with django_db_blocker.unblock():
        load_deals()

Result:

============ test session starts =============
myapp/tests/pytest_test.py::test_1 PASSED
myapp/tests/pytest_test.py::test_2 FAILED

The issue is with the DB not being persisted, as I cann see I can access the created deal if I return it in the fixture, but the DB is empty.

========= FAILURES =========
_________ test_2 _________

myfixture = id: 1, name='Alice'

    @pytest.mark.django_db
    def test_2(myfixture):
        print(myfixture)
        deals = Deal.objects.all()
>       assert deals
E       assert <QuerySet []>

And if I run only test_2 it works of course:

============ test session starts =============
myapp/tests/pytest_test.py::test_2 PASSED

I've got many tests that share the same fixture, it would be a lot faster if the fixture could run only once as the load_deals() is quite slow.

It looks like I can reuse the name django_db_setup as a fixture, and the scope='session' works, but I need to run different fixtures depending on the tests.

I'm using python 3.6.1 - pytest 3.1.2 - pytest-django 3.1.2 with mariadb 10.1

Any idea on how to make this work?

ludofet
  • 307
  • 3
  • 14

1 Answers1

0

Doesn't look like a pytest problem.

What this object Deal ? Look into it's class implementation.

Also look into your db functionality and function load_deals() definition.

Try adding some logging to see if Deal.objects changes after calling Deal.objects.all()

Is it possible that those Deal.objects is instance of a generator? (generators in python are disposable (for single use only) objects:

>>> g = ( i for i in range(20))
>>> g
<generator object <genexpr> at 0x101a16f10>
>>> [i for i in g]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> [i for i in g]  # g "has nothing to give"
[]

Also see this answer: Why can't I iterate twice over the same data?

@ludofet OK i see the docs from django now - Deal.objects.all() will return a QuerySet object which is a subclass from iterable - which is a "single use object" meaning you can't iterate twice over it. Hence first test that uses Deal object will pass and second will not find any data in it. Your tests pass when your fixture is of a scope "function" because you fixture creates new Deal object for each test and hence you iterate over it only once. https://docs.djangoproject.com/en/1.11/ref/models/querysets/#all https://docs.djangoproject.com/en/1.11/ref/models/querysets/#django.db.models.query.QuerySet

Dmitry Tokarev
  • 1,851
  • 15
  • 29
  • Deal is a django model. I've update the example to clarify it. The Deal.objects.all() is a django function to get all Deal from the database. No transformation is done. – ludofet Jul 02 '17 at 09:09
  • I don't think I agree with the explanation. The `new_deal.save()` should persist the data into the test database. Now in each test when calling `Deal.objects.all()` I should get an iterator (here we agree :-) ) but it should be a new one from the db each time. I think the issue here is with the management of pytest-django and how transactions are managed in the pytest.mark.django_db fixture that is a function scope. – ludofet Jan 03 '22 at 08:31