Home > Software design >  How to reuse database in some tests using `pytest-django`?
How to reuse database in some tests using `pytest-django`?

Time:12-28

According to the documentation, --reuse-db should be used or addopts = --reuse-db in pytest.ini. I tried both and they don't work. The current tests have to signup and authenticate a new user at the start of each test to be able to access features requiring login. This makes tests run slowly and with the number of tests growing, this is becoming less convenient. Here's an example to demonstrate:

@pytest.mark.django_db
class TestSignIn:
    def test_valid_user(self, client, valid_user_data, django_user_model, django_db_keepdb):
        client_signup(client, valid_user_data, django_db_keepdb)
        response = activate_user(client)
        assert 'sign out' in get_text(response)

    def test_sign_in(self, client, valid_user_data, django_user_model, django_db_keepdb):
        client_signup(client, valid_user_data)
        activate_user(client)
        response = client.post(reverse('signout'), follow=True)
        assert 'sign in' in get_text(response)
        response = client_sign_in(client, valid_user_data)
        assert 'sign out' in get_text(response)

In test_valid_user, a new user is created and after user activation, a user shows up:

>>> django_user_model.objects.count()
1

and I verify using:

>>> django_db_keepdb
True

I want to reuse the user created in the previous test instead of creating a new one. With --reuse-db specified, I'm expecting the second test test_sign_in to detect the very same user. Instead, I get:

>>> django_user_model.objects.count()
0

CodePudding user response:

The problem is that the django_db_keepdb fixture is used to indicate that the database should not be destroyed after the test has completed. It does not prevent the database from being reset between tests. In order to reuse the same user between tests, you will need to use the django_db_reset_sequences fixture. This fixture will reset the database and keep the data, allowing you to reuse the same data in subsequent tests.

django_db_reset_sequences fixture in action:

@pytest.mark.django_db
class TestSignIn:
    def test_valid_user(self, client, valid_user_data, django_user_model, django_db_reset_sequences):
        client_signup(client, valid_user_data)
        response = activate_user(client)
        assert 'sign out' in get_text(response)

def test_sign_in(self, client, valid_user_data, django_user_model, django_db_reset_sequences):
    client_signup(client, valid_user_data)
    activate_user(client)
    response = client.post(reverse('signout'), follow=True)
    assert 'sign in' in get_text(response)
    response = client_sign_in(client, valid_user_data)
    assert 'sign out' in get_text(response)

You will also need to include the django_db_reset_sequences fixture in the list of arguments for each test function. This will reset the database and keep the data between tests, allowing you to reuse the same user in both tests.

CodePudding user response:

@pytest.mark.django_db marker runs the function-scoped _django_db_helper fixture.

Implement @pytest.mark.django_db_class_scope marker that runs a class-scoped fixture:

import pytest
from pytest_django.fixtures import _django_db_helper

_django_db_class_scope_helper = pytest.fixture(
    fixture_function=_django_db_helper.__wrapped__,
    scope='class',
    name='_django_db_class_scope_helper',
)


@pytest.fixture(autouse=True)
def django_db_class_scope_marker(request) -> None:
    marker = request.node.get_closest_marker('django_db_class_scope')
    if marker:
        request.getfixturevalue('_django_db_class_scope_helper')

Usage:

# @pytest.mark.django_db
@pytest.mark.django_db_class_scope
class TestSignIn:
  • Related