Skip to content

Commit de43b3b

Browse files
aaugustinpelme
authored andcommitted
Add support for serialized rollback. Fix pytest-dev#329.
WORK IN PROGRESS - UNTESTED
1 parent b6c529d commit de43b3b

File tree

4 files changed

+61
-11
lines changed

4 files changed

+61
-11
lines changed

docs/changelog.rst

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@ Changelog
33

44
NEXT
55
----
6-
Bug fixes
7-
^^^^^^^^^
8-
9-
* Fix error when Django happens to be imported before pytest-django runs.
10-
Thanks to Will Harris for `the bug report
11-
<https://github.com/pytest-dev/pytest-django/issues/289>`_.
126

137
Features
148
^^^^^^^^
9+
* Add support for serialized rollback in transactional tests.
10+
Thanks to Piotr Karkut for `the bug report
11+
<https://github.com/pytest-dev/pytest-django/issues/329>`_.
12+
1513
* Added a new option `--migrations` to negate a default usage of
1614
`--nomigrations`.
1715

16+
Bug fixes
17+
^^^^^^^^^
18+
* Fix error when Django happens to be imported before pytest-django runs.
19+
Thanks to Will Harris for `the bug report
20+
<https://github.com/pytest-dev/pytest-django/issues/289>`_.
1821
2.9.1
1922
-----
2023

docs/helpers.rst

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ on what marks are and for notes on using_ them.
1616
``pytest.mark.django_db`` - request database access
1717
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1818

19-
.. py:function:: pytest.mark.django_db([transaction=False])
19+
.. py:function:: pytest.mark.django_db([transaction=False, serialized_rollback=False])
2020
2121
This is used to mark a test function as requiring the database. It
2222
will ensure the database is setup correctly for the test. Each test
@@ -38,6 +38,14 @@ on what marks are and for notes on using_ them.
3838
uses. When ``transaction=True``, the behavior will be the same as
3939
`django.test.TransactionTestCase`_
4040

41+
:type serialized_rollback: bool
42+
:param serialized_rollback:
43+
The ``serialized_rollback`` argument enables `rollback emulation`_.
44+
After a `django.test.TransactionTestCase`_ runs, the database is
45+
flushed, destroying data created in data migrations. This is the
46+
default behavior of Django. Setting ``serialized_rollback=True``
47+
tells Django to restore that data.
48+
4149
.. note::
4250

4351
If you want access to the Django database *inside a fixture*
@@ -54,6 +62,7 @@ on what marks are and for notes on using_ them.
5462
Test classes that subclass Python's ``unittest.TestCase`` need to have the
5563
marker applied in order to access the database.
5664

65+
.. _rollback emulation: https://docs.djangoproject.com/en/stable/topics/testing/overview/#rollback-emulation
5766
.. _django.test.TestCase: https://docs.djangoproject.com/en/dev/topics/testing/overview/#testcase
5867
.. _django.test.TransactionTestCase: https://docs.djangoproject.com/en/dev/topics/testing/overview/#transactiontestcase
5968

@@ -191,6 +200,16 @@ transaction support. This is only required for fixtures which need
191200
database access themselves. A test function would normally use the
192201
:py:func:`~pytest.mark.django_db` mark to signal it needs the database.
193202

203+
``serialized_rollback``
204+
~~~~~~~~~~~~~~~~~~~~~~~
205+
206+
When the ``transactional_db`` fixture is enabled, this fixture can be
207+
added to trigger `rollback emulation`_ and thus restores data created
208+
in data migrations after each transaction test. This is only required
209+
for fixtures which need to enforce this behavior. A test function
210+
would use :py:func:`~pytest.mark.django_db(serialized_rollback=True)`
211+
to request this behavior.
212+
194213
``live_server``
195214
~~~~~~~~~~~~~~~
196215

@@ -200,6 +219,12 @@ or by requesting it's string value: ``unicode(live_server)``. You can
200219
also directly concatenate a string to form a URL: ``live_server +
201220
'/foo``.
202221

222+
Since the live server and the tests run in different threads, they
223+
cannot share a database transaction. For this reason, ``live_server``
224+
depends on the ``transactional_db`` fixture. If tests depend on data
225+
created in data migrations, you should add the ``serialized_rollback``
226+
fixture.
227+
203228
``settings``
204229
~~~~~~~~~~~~
205230

pytest_django/fixtures.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ def teardown_database():
6464
request.addfinalizer(teardown_database)
6565

6666

67-
def _django_db_fixture_helper(transactional, request, _django_cursor_wrapper):
67+
def _django_db_fixture_helper(transactional, serialized_rollback,
68+
request, _django_cursor_wrapper):
6869
if is_django_unittest(request):
6970
return
7071

@@ -105,6 +106,7 @@ def flushdb():
105106

106107
if django_case:
107108
case = django_case(methodName='__init__')
109+
case.serialized_rollback = serialized_rollback
108110
case._pre_setup()
109111
request.addfinalizer(case._post_teardown)
110112

@@ -178,7 +180,9 @@ def db(request, _django_db_setup, _django_cursor_wrapper):
178180
or 'live_server' in request.funcargnames:
179181
request.getfuncargvalue('transactional_db')
180182
else:
181-
_django_db_fixture_helper(False, request, _django_cursor_wrapper)
183+
_django_db_fixture_helper(
184+
transactional=False, serialized_rollback=False,
185+
request=request, _django_cursor_wrapper=_django_cursor_wrapper)
182186

183187

184188
@pytest.fixture(scope='function')
@@ -193,7 +197,22 @@ def transactional_db(request, _django_db_setup, _django_cursor_wrapper):
193197
database setup will behave as only ``transactional_db`` was
194198
requested.
195199
"""
196-
_django_db_fixture_helper(True, request, _django_cursor_wrapper)
200+
# TODO -- is request.getfuncargvalue('serialized_rollback') enough
201+
# to add 'serialized_rollback' to request.funcargnames?
202+
serialized_rollback = 'serialized_rollback' in request.funcargnames
203+
_django_db_fixture_helper(
204+
transactional=True, serialized_rollback=serialized_rollback,
205+
request=request, _django_cursor_wrapper=_django_cursor_wrapper)
206+
207+
208+
@pytest.fixture(scope='function')
209+
def serialized_rollback(request):
210+
"""Enable serialized rollback after transaction test cases
211+
212+
This fixture only has an effect when the ``transactional_db``
213+
fixture is active, which happen as a side-effect of requesting
214+
``live_server``.
215+
"""
197216

198217

199218
@pytest.fixture()

pytest_django/plugin.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,8 @@ def _django_db_marker(request):
375375
request.getfuncargvalue('transactional_db')
376376
else:
377377
request.getfuncargvalue('db')
378+
if marker.serialized_rollback:
379+
request.getfuncargvalue('serialized_rollback')
378380

379381

380382
@pytest.fixture(autouse=True, scope='class')
@@ -567,8 +569,9 @@ def validate_django_db(marker):
567569
It checks the signature and creates the `transaction` attribute on
568570
the marker which will have the correct value.
569571
"""
570-
def apifun(transaction=False):
572+
def apifun(transaction=False, serialized_rollback=False):
571573
marker.transaction = transaction
574+
marker.serialized_rollback = serialized_rollback
572575
apifun(*marker.args, **marker.kwargs)
573576

574577

0 commit comments

Comments
 (0)