Skip to content

Support reset_sequences #308

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
51bd265
Implement the reset_sequences_db fixture.
cb109 Jan 16, 2016
3cad678
Implement the reset_sequences option for the django_db mark.
cb109 Jan 16, 2016
df1bbdb
Implement tests for the reset_sequences mark option and fixture.
cb109 Jan 16, 2016
8c0dce4
Update the docs about the reset_sequences support.
cb109 Jan 16, 2016
9b7ec4b
Get the marker through the request object instead of using hardcoded …
cb109 Jan 16, 2016
e45daf3
Add missing checks for transaction support within tests.
cb109 Jan 16, 2016
50001d3
Skip reset_sequences tests for django versions that do not support it.
cb109 Jan 16, 2016
216757f
Add punctuation and fix some docstring grammar.
cb109 Jan 16, 2016
def0121
Fix db access fixture precedence.
cb109 Jan 16, 2016
c6ec611
Update docs with a note about combining db access fixtures.
cb109 Jan 16, 2016
3e3287a
Update docs about marker option usage.
cb109 Jan 17, 2016
add9d49
Do not return the TestCase instance when requesting db fixtures.
cb109 Jul 1, 2016
eaa5e7b
Rework reset_sequences_db test.
cb109 Jul 1, 2016
da378e2
Fix start id value.
cb109 Jul 1, 2016
a756f4e
Fix conflicts within docs, fixtures and plugin definition.
cb109 Jul 4, 2016
0bfb9af
Satisfy QA checks.
cb109 Jul 4, 2016
b74c28d
Remove kwargs from headline for a more readable list of contents.
cb109 Jul 4, 2016
28a3876
Fix version check against '1.10'.
cb109 Jul 14, 2016
f8d2e1d
Remove trailing whitespace.
cb109 Jul 14, 2016
fd3603a
Rename fixture to `django_db_reset_sequences`.
cb109 Jul 14, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 41 additions & 12 deletions docs/helpers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@ on what marks are and for notes on using_ them.
.. _using: http://pytest.org/latest/example/markers.html#marking-whole-classes-or-modules


``pytest.mark.django_db(transaction=False)`` - request database access
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``pytest.mark.django_db`` - request database access
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. :py:function:: pytest.mark.django_db:
.. py:function:: pytest.mark.django_db([transaction=False, reset_sequences=False])

This is used to mark a test function as requiring the database. It
will ensure the database is setup correctly for the test. Each test
will ensure the database is setup correctly for the test. Each test
will run in its own transaction which will be rolled back at the end
of the test. This behavior is the same as Django's standard
`django.test.TestCase`_ class.

In order for a test to have access to the database it must either
be marked using the ``django_db`` mark or request one of the ``db``
or ``transactional_db`` fixtures. Otherwise the test will fail
when trying to access the database.
be marked using the ``django_db`` mark or request one of the ``db``,
``transactional_db`` or ``reset_sequences_db`` fixtures. Otherwise the
test will fail when trying to access the database.

:type transaction: bool
:param transaction:
Expand All @@ -38,14 +38,23 @@ when trying to access the database.
uses. When ``transaction=True``, the behavior will be the same as
`django.test.TransactionTestCase`_


:type reset_sequences: bool
:param reset_sequences:
The ``reset_sequences`` argument will ask to reset auto increment sequence
values (e.g. primary keys) before running the test. Defaults to
``False``. Must be used together with ``transaction=True`` to have an
effect. Please be aware that not all databases support this feature.
For details see `django.test.TransactionTestCase.reset_sequences`_

.. note::

If you want access to the Django database *inside a fixture*
this marker will not help even if the function requesting your
fixture has this marker applied. To access the database in a
fixture, the fixture itself will have to request the ``db`` or
``transactional_db`` fixture. See below for a description of
them.
fixture, the fixture itself will have to request the ``db``,
``transactional_db`` or ``reset_sequences_db`` fixture. See below
for a description of them.

.. note:: Automatic usage with ``django.test.TestCase``.

Expand All @@ -54,6 +63,7 @@ when trying to access the database.
Test classes that subclass Python's ``unittest.TestCase`` need to have the
marker applied in order to access the database.

.. _django.test.TransactionTestCase.reset_sequences: https://docs.djangoproject.com/en/dev/topics/testing/advanced/#django.test.TransactionTestCase.reset_sequences
.. _django.test.TestCase: https://docs.djangoproject.com/en/dev/topics/testing/overview/#testcase
.. _django.test.TransactionTestCase: https://docs.djangoproject.com/en/dev/topics/testing/overview/#transactiontestcase

Expand Down Expand Up @@ -190,8 +200,17 @@ mark to signal it needs the database.

This fixture can be used to request access to the database including
transaction support. This is only required for fixtures which need
database access themselves. A test function would normally use the
:py:func:`~pytest.mark.django_db` mark to signal it needs the database.
database access themselves. A test function should normally use the
:py:func:`~pytest.mark.django_db` mark with ``transaction=True``.

``reset_sequences_db``
~~~~~~~~~~~~~~~~~~~~

This fixture provides the same transactional database access as
``transactional_db``, with additional support for reset of auto increment
sequences (if your database supports it). This is only required for
fixtures which need database access themselves. A test function should
normally use the :py:func:`~pytest.mark.django_db` mark with ``transaction=True`` and ``reset_sequences=True``.

``live_server``
~~~~~~~~~~~~~~~
Expand All @@ -202,6 +221,16 @@ or by requesting it's string value: ``unicode(live_server)``. You can
also directly concatenate a string to form a URL: ``live_server +
'/foo``.

.. note:: Combining database access fixtures.

When using multiple database fixtures together, only one of them is
used. Their order of precedence is as follows (the last one wins):
* ``db``
* ``transactional_db``
* ``reset_sequences_db``
In addition, using ``live_server`` will also trigger transactional
database access, if not specified.

``settings``
~~~~~~~~~~~~

Expand Down
72 changes: 52 additions & 20 deletions pytest_django/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@

from .lazy_django import get_django_version, skip_if_no_django

__all__ = ['django_db_setup', 'db', 'transactional_db', 'admin_user',
'django_user_model', 'django_username_field',
'client', 'admin_client', 'rf', 'settings', 'live_server',
'_live_server_helper']
__all__ = ['django_db_setup', 'db', 'transactional_db',
'django_db_reset_sequences', 'admin_user', 'django_user_model',
'django_username_field', 'client', 'admin_client', 'rf',
'settings', 'live_server', '_live_server_helper']


@pytest.fixture(scope='session')
Expand Down Expand Up @@ -58,7 +58,8 @@ def django_db_use_migrations(request):

@pytest.fixture(scope='session')
def django_db_keepdb(request):
return request.config.getvalue('reuse_db') and not request.config.getvalue('create_db')
return (request.config.getvalue('reuse_db') and
not request.config.getvalue('create_db'))


@pytest.fixture(scope='session')
Expand Down Expand Up @@ -97,14 +98,16 @@ def django_db_setup(

def teardown_database():
with django_db_blocker:
(DiscoverRunner(verbosity=pytest.config.option.verbose, interactive=False)
(DiscoverRunner(verbosity=pytest.config.option.verbose,
interactive=False)
.teardown_databases(db_cfg))

if not django_db_keepdb:
request.addfinalizer(teardown_database)


def _django_db_fixture_helper(transactional, request, django_db_blocker):
def _django_db_fixture_helper(request, django_db_blocker,
transactional=False, reset_sequences=False):
if is_django_unittest(request):
return

Expand All @@ -117,6 +120,11 @@ def _django_db_fixture_helper(transactional, request, django_db_blocker):

if transactional:
from django.test import TransactionTestCase as django_case

if reset_sequences:
class ResetSequenceTestCase(django_case):
reset_sequences = True
django_case = ResetSequenceTestCase
else:
from django.test import TestCase as django_case

Expand All @@ -136,7 +144,7 @@ def _disable_native_migrations():

@pytest.fixture(scope='function')
def db(request, django_db_setup, django_db_blocker):
"""Require a django test database
"""Require a django test database.

This database will be setup with the default fixtures and will have
the transaction management disabled. At the end of the test the outer
Expand All @@ -145,30 +153,54 @@ def db(request, django_db_setup, django_db_blocker):
This is more limited than the ``transactional_db`` resource but
faster.

If both this and ``transactional_db`` are requested then the
database setup will behave as only ``transactional_db`` was
requested.
If multiple database fixtures are requested, they take precedence
over each other in the following order (the last one wins): ``db``,
``transactional_db``, ``django_db_reset_sequences``.
"""
if 'transactional_db' in request.funcargnames \
or 'live_server' in request.funcargnames:
if 'django_db_reset_sequences' in request.funcargnames:
request.getfuncargvalue('django_db_reset_sequences')
if ('transactional_db' in request.funcargnames or
'live_server' in request.funcargnames):
request.getfuncargvalue('transactional_db')
else:
_django_db_fixture_helper(False, request, django_db_blocker)
_django_db_fixture_helper(request, django_db_blocker,
transactional=False)


@pytest.fixture(scope='function')
def transactional_db(request, django_db_setup, django_db_blocker):
"""Require a django test database with transaction support
"""Require a django test database with transaction support.

This will re-initialise the django database for each test and is
thus slower than the normal ``db`` fixture.

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 ``transactional_db`` was
requested.
this resource.

If multiple database fixtures are requested, they take precedence
over each other in the following order (the last one wins): ``db``,
``transactional_db``, ``django_db_reset_sequences``.
"""
if 'django_db_reset_sequences' in request.funcargnames:
request.getfuncargvalue('django_db_reset_sequences')
_django_db_fixture_helper(request, django_db_blocker,
transactional=True)


@pytest.fixture(scope='function')
def django_db_reset_sequences(request, django_db_setup, django_db_blocker):
"""Require a transactional test database with sequence reset support.

This behaves like the ``transactional_db`` fixture, with the addition
of enforcing a reset of all auto increment sequences. If the enquiring
test relies on such values (e.g. ids as primary keys), you should
request this resource to ensure they are consistent across tests.

If multiple database fixtures are requested, they take precedence
over each other in the following order (the last one wins): ``db``,
``transactional_db``, ``django_db_reset_sequences``.
"""
_django_db_fixture_helper(True, request, django_db_blocker)
_django_db_fixture_helper(request, django_db_blocker,
transactional=True, reset_sequences=True)


@pytest.fixture()
Expand Down
20 changes: 14 additions & 6 deletions pytest_django/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from .fixtures import django_user_model # noqa
from .fixtures import django_username_field # noqa
from .fixtures import live_server # noqa
from .fixtures import django_db_reset_sequences # noqa
from .fixtures import rf # noqa
from .fixtures import settings # noqa
from .fixtures import transactional_db # noqa
Expand Down Expand Up @@ -363,13 +364,15 @@ def django_db_blocker():
def _django_db_marker(request):
"""Implement the django_db marker, internal to pytest-django.

This will dynamically request the ``db`` or ``transactional_db``
fixtures as required by the django_db marker.
This will dynamically request the ``db``, ``transactional_db`` or
``django_db_reset_sequences`` fixtures as required by the django_db marker.
"""
marker = request.keywords.get('django_db', None)
if marker:
validate_django_db(marker)
if marker.transaction:
if marker.reset_sequences:
request.getfuncargvalue('django_db_reset_sequences')
elif marker.transaction:
request.getfuncargvalue('transactional_db')
else:
request.getfuncargvalue('db')
Expand Down Expand Up @@ -576,11 +579,16 @@ def __exit__(self, exc_type, exc_value, traceback):
def validate_django_db(marker):
"""Validate the django_db marker.

It checks the signature and creates the `transaction` attribute on
the marker which will have the correct value.
It checks the signature and creates the ``transaction`` and
``reset_sequences`` attributes on the marker which will have the
correct values.

A sequence reset is only allowed when combined with a transaction.
"""
def apifun(transaction=False):
def apifun(transaction=False, reset_sequences=False):
marker.transaction = transaction
marker.reset_sequences = transaction and reset_sequences

apifun(*marker.args, **marker.kwargs)


Expand Down
Loading