Skip to content

Commit d4de772

Browse files
aaugustinblueyed
authored andcommitted
Add support for serialized rollback
Fix pytest-dev#329.
1 parent ad25f17 commit d4de772

File tree

4 files changed

+59
-5
lines changed

4 files changed

+59
-5
lines changed

docs/changelog.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ Bug fixes
1010
Thanks to Will Harris for `the bug report
1111
<https://github.com/pytest-dev/pytest-django/issues/289>`_.
1212

13+
Features
14+
^^^^^^^^
15+
* Add support for serialized rollback in transactional tests.
16+
Thanks to Piotr Karkut for `the bug report
17+
<https://github.com/pytest-dev/pytest-django/issues/329>`_.
18+
1319
Features
1420
^^^^^^^^
1521
* Added a new option `--migrations` to negate a default usage of

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: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ def teardown_database():
6363
request.addfinalizer(teardown_database)
6464

6565

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

@@ -83,6 +84,7 @@ def _django_db_fixture_helper(transactional, request, _django_cursor_wrapper):
8384

8485
if django_case:
8586
case = django_case(methodName='__init__')
87+
case.serialized_rollback = serialized_rollback
8688
case._pre_setup()
8789
request.addfinalizer(case._post_teardown)
8890

@@ -115,7 +117,9 @@ def db(request, _django_db_setup, _django_cursor_wrapper):
115117
or 'live_server' in request.funcargnames:
116118
request.getfuncargvalue('transactional_db')
117119
else:
118-
_django_db_fixture_helper(False, request, _django_cursor_wrapper)
120+
_django_db_fixture_helper(
121+
transactional=False, serialized_rollback=False,
122+
request=request, _django_cursor_wrapper=_django_cursor_wrapper)
119123

120124

121125
@pytest.fixture(scope='function')
@@ -130,7 +134,23 @@ def transactional_db(request, _django_db_setup, _django_cursor_wrapper):
130134
database setup will behave as only ``transactional_db`` was
131135
requested.
132136
"""
133-
_django_db_fixture_helper(True, request, _django_cursor_wrapper)
137+
# TODO -- is request.getfuncargvalue('serialized_rollback') enough
138+
# to add 'serialized_rollback' to request.funcargnames?
139+
serialized_rollback = 'serialized_rollback' in request.funcargnames
140+
_django_db_fixture_helper(transactional=True,
141+
serialized_rollback=serialized_rollback,
142+
request=request,
143+
_django_cursor_wrapper=_django_cursor_wrapper)
144+
145+
146+
@pytest.fixture(scope='function')
147+
def serialized_rollback(request):
148+
"""Enable serialized rollback after transaction test cases
149+
150+
This fixture only has an effect when the ``transactional_db``
151+
fixture is active, which happen as a side-effect of requesting
152+
``live_server``.
153+
"""
134154

135155

136156
@pytest.fixture()

pytest_django/plugin.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,8 @@ def _django_db_marker(request):
361361
request.getfuncargvalue('transactional_db')
362362
else:
363363
request.getfuncargvalue('db')
364+
if marker.serialized_rollback:
365+
request.getfuncargvalue('serialized_rollback')
364366

365367

366368
@pytest.fixture(autouse=True, scope='class')
@@ -559,8 +561,9 @@ def validate_django_db(marker):
559561
It checks the signature and creates the `transaction` attribute on
560562
the marker which will have the correct value.
561563
"""
562-
def apifun(transaction=False):
564+
def apifun(transaction=False, serialized_rollback=False):
563565
marker.transaction = transaction
566+
marker.serialized_rollback = serialized_rollback
564567
apifun(*marker.args, **marker.kwargs)
565568

566569

0 commit comments

Comments
 (0)