pytest-django icon indicating copy to clipboard operation
pytest-django copied to clipboard

django_db_setup runs inside transactional_db transaction

Open OrangeDog opened this issue 6 years ago • 16 comments

If the first db test that gets run happens to have transactional_db (or django_db(transaction=True)) then all subsequent db tests will fail.

This appears to be because the db setup (migrations etc.) are all rolled-back and will not run again because django_db_setup is session-scoped.

OrangeDog avatar May 11 '18 10:05 OrangeDog

Hitting this as well. It appears that fixture data loaded in django_db_setup gets removed for all subsequent tests by a test that runs with @pytest.mark.django_db(transaction=True).

We're loading fixture data in django_db_setup as described in the docs:

# conftest.py
import pytest
from django.core.management import call_command
@pytest.fixture(scope='session')
def django_db_setup(django_db_setup, django_db_blocker):
    with django_db_blocker.unblock():
        call_command('loaddata', 'myfixture.json')

Tests that run prior to the transaction=True test are able to access the fixture data with no problem...

@pytest.mark.django_db
def test_data_presence():
    assert MyModel.objects.first() is not None # Passes

... but once we run a transaction test case, subsequent tests are missing the fixture data.

@pytest.mark.django_db(transaction=True)
def test_transaction_behavior():
    ...

@pytest.mark.django_db
def test_data_presence2():
    assert MyModel.objects.first() is not None # Fails

If whatever subset of tests we run (using e.g. py.test -k "expr" or py.test -m "expr") happens to skip all of the transaction=True test cases, the fixture remains in place.

yourcelf avatar Aug 23 '18 15:08 yourcelf

IIRC "serialized_rollback" is required for this, which I've started a while ago in https://github.com/pytest-dev/pytest-django/pull/353 - maybe you want to pick it up?

blueyed avatar Aug 23 '18 17:08 blueyed

@yourcelf Have you tried doing your loaddata in an overwritten transactional_db fixture?

blueyed avatar Aug 23 '18 17:08 blueyed

Also relevant: https://github.com/pytest-dev/pytest-django/issues/214

blueyed avatar Aug 23 '18 17:08 blueyed

Thanks for the suggestions @blueyed. I tried adding this definition to conftest.py:

import pytest
from django.core.management import call_command
from pytest_django.fixtures import transactional_db as orig_transactional_db

@pytest.fixture(scope='session')
def django_db_setup(django_db_setup, django_db_blocker):
    with django_db_blocker.unblock():
        call_command('loaddata', 'myfixture.json')

@pytest.fixture(scope='function')
def transactional_db(request, django_db_setup, django_db_blocker):
    orig_transactional_db(request, django_db_setup, django_db_blocker)
    with django_db_blocker.unblock():
        call_command('loaddata', 'myfixture.json')

Is that what you meant by loading data in an overwritten transactional_db fixture?

This didn't have any change in the availability of fixture data after running a test with @pytest.mark.django_db(transaction=True) -- the fixture data was still missing. (This was the same whether the "super" call to orig_transactional_db happened first or last.)

yourcelf avatar Aug 30 '18 19:08 yourcelf

Try just that:

import pytest
from django.core.management import call_command

@pytest.fixture(scope='function')
def transactional_db(transactiona_db, request, django_db_setup, django_db_blocker):
    with django_db_blocker.unblock():
        call_command('loaddata', 'myfixture.json')

blueyed avatar Aug 30 '18 20:08 blueyed

No change... with only the transactional_db override in conftest.py, the fixture was unavailable to regular @pytest.mark.django_db tests (without transaction=True), no matter what order they run in. With both the transactional_db override you suggested here and django_db_setup defined, the behavior is the same -- tests running before a transaction=True test work fine, those running after are missing the fixture.

yourcelf avatar Aug 30 '18 23:08 yourcelf

with only the transactional_db override in conftest.py, the fixture was unavailable to regular @pytest.mark.django_db tests (without transaction=True), no matter what order they run in.

That is expected.

Too bad it does not work. Have you verified that the loaddata command gets run at least for each test? You could enable logging of DB queries and then see from there what gets done for a) the first test where it is available, and then b) for the second test where it is missing - and then compare them.

blueyed avatar Aug 31 '18 00:08 blueyed

I think I have a similar problem. I have a Django app with a FooBarModel and a have a data migration that includes a single record on it with very specific data. For various reasons, I have tests for database constraints. I also have some FooBarModel records that are created automatically when some other models instances are created.

I have two separate test functions, and both with @pytest.mark.django_db (transaction=True)

The first function test all database constraints of the FooBarModel.

The second function test if the automatic records are created as expected. (I use a fixture to create instances of the other models, and by consequence will some records in FooBarModel table)

after the first test function is executed, the database no longer has the data included by the data migration (apparently this data was removed) and because of this, the second function fails to verify that the data included in the migration exists in the database.

I expected @pytest.mark.django_db (transaction=True) to cause, in each test case (I also use @ pytest.mark.parametrize) the database to return to the state it was in after running all migrations and data migrations and not being totally data cleaned.

An update: I use MySQL

luzfcb avatar Apr 25 '19 01:04 luzfcb

Hi @luzfcb I'm experiencing exactly the same issues. Do you have any suggestions as to how to fix it?

svechinsky avatar May 27 '19 11:05 svechinsky

Same here!

lucas-bremond avatar Aug 06 '19 06:08 lucas-bremond

Hello, currently encountering this problem - is there a workaround that anyone is aware of please? Thank you

sbmooc avatar Nov 14 '19 10:11 sbmooc

This probably can be solved by adding support to serialized_rollback

On pull-request #721 I have tried to put together some of the solutions, but the approach is not yet 100% functional. It would be nice to have new eyes on that pull-request.

luzfcb avatar Nov 14 '19 14:11 luzfcb

I'm experiencing exactly the same issues. I was trying to switch from In-Memory SQLite DB to file SQLite DB, because data provisioning in In-Memory SQLite DB takes too much time, and I want to reuse DB with already provisioned data instead. File SQLite DB doesn't work with db fixture, so I used transactional_db but ran into this issue. First test pass and then all DB is erased. What about serialized_rollback current feature implementation status? Or is there a better approach ? I was thinking of simply saving/restoring SQLite DB file before each test (probably much faster than serialized_rollback).

frossigneux avatar May 11 '20 08:05 frossigneux

We have also observed this issue. Are there workarounds know to avoid this issue? Some solutions perhaps?

ibqn avatar Oct 16 '20 17:10 ibqn

I reproduced this issue and can confirm that it is fixed in #970, using @pytest.mark.django_db(serialized_rollback=True). Tested on pytest-django 4.5.2.

benjaoming avatar Dec 22 '21 00:12 benjaoming