diff --git a/docs/changelog.rst b/docs/changelog.rst index c90a5f3d3..d76c717fa 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,11 @@ Changelog ========= +NEXT +---- + +* Fix interaction between ``db`` and ``transaction_db`` fixtures (#126). + 2.6.2 ----- diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 8de9e6393..d5a317468 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -59,6 +59,39 @@ def teardown_database(): if not request.config.getvalue('reuse_db'): request.addfinalizer(teardown_database) +def _django_db_fixture_helper(transactional, request, _django_cursor_wrapper): + if is_django_unittest(request.node): + return + + if transactional: + _django_cursor_wrapper.enable() + + def flushdb(): + """Flush the database and close database connections""" + # Django does this by default *before* each test + # instead of after. + from django.db import connections + from django.core.management import call_command + + for db in connections: + call_command('flush', verbosity=0, + interactive=False, database=db) + for conn in connections.all(): + conn.close() + + request.addfinalizer(_django_cursor_wrapper.disable) + request.addfinalizer(flushdb) + else: + if 'live_server' in request.funcargnames: + return + from django.test import TestCase + + _django_cursor_wrapper.enable() + _django_cursor_wrapper._is_transactional = False + case = TestCase(methodName='__init__') + case._pre_setup() + request.addfinalizer(_django_cursor_wrapper.disable) + request.addfinalizer(case._post_teardown) ################ User visible fixtures ################ @@ -70,24 +103,16 @@ def db(request, _django_db_setup, _django_cursor_wrapper): This database will be setup with the default fixtures and will have the transaction management disabled. At the end of the test the transaction will be rolled back to undo any changes to the - database. This is more limited then the ``transaction_db`` + database. This is more limited then the ``transactional_db`` resource but faster. - If both this and ``transaction_db`` are requested then the - database setup will behave as only ``transaction_db`` was + If both this and ``transactional_db`` are requested then the + database setup will behave as only ``transactional_db`` was requested. """ - if ('transactional_db' not in request.funcargnames and - 'live_server' not in request.funcargnames and - not is_django_unittest(request.node)): - - from django.test import TestCase - - _django_cursor_wrapper.enable() - case = TestCase(methodName='__init__') - case._pre_setup() - request.addfinalizer(_django_cursor_wrapper.disable) - request.addfinalizer(case._post_teardown) + if 'transactional_db' in request.funcargnames: + return request.getfuncargvalue('transactional_db') + return _django_db_fixture_helper(False, request, _django_cursor_wrapper) @pytest.fixture(scope='function') @@ -99,27 +124,10 @@ def transactional_db(request, _django_db_setup, _django_cursor_wrapper): If you want to use the database with transactions you must request this resource. If both this and ``db`` are requested then the - database setup will behave as only ``transaction_db`` was + database setup will behave as only ``transactional_db`` was requested. """ - if not is_django_unittest(request.node): - _django_cursor_wrapper.enable() - - def flushdb(): - """Flush the database and close database connections""" - # Django does this by default *before* each test - # instead of after. - from django.db import connections - from django.core.management import call_command - - for db in connections: - call_command('flush', verbosity=0, - interactive=False, database=db) - for conn in connections.all(): - conn.close() - - request.addfinalizer(_django_cursor_wrapper.disable) - request.addfinalizer(flushdb) + return _django_db_fixture_helper(True, request, _django_cursor_wrapper) @pytest.fixture() diff --git a/tests/test_database.py b/tests/test_database.py index d414e2433..54cb08508 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -9,13 +9,13 @@ def noop_transactions(): - """Test whether transactions are disabled + """Test whether transactions are disabled. Return True if transactions are disabled, False if they are enabled. """ - # Newer versions of Django simply runs standard tests in an atomic block. + # Newer versions of Django simply run standard tests in an atomic block. if hasattr(connection, 'in_atomic_block'): return connection.in_atomic_block else: @@ -109,6 +109,28 @@ def test_fin(self, fin): pass +class TestDatabaseFixturesBothOrder: + @pytest.fixture + def fixture_with_db(self, db): + Item.objects.create(name='spam') + + @pytest.fixture + def fixture_with_transdb(self, transactional_db): + Item.objects.create(name='spam') + + def test_trans(self, fixture_with_transdb): + pass + + def test_db(self, fixture_with_db): + pass + + def test_db_trans(self, fixture_with_db, fixture_with_transdb): + pass + + def test_trans_db(self, fixture_with_transdb, fixture_with_db): + pass + + class TestDatabaseMarker: @pytest.mark.django_db