Search code examples
pythondjangopytestdjango-testing

How can I prevent pytest to remove database records between test cases?


I use pre-created postgres database for my tests. Here the pytest setup:
pytest.ini:

[pytest]
norecursedirs = frontend static .svn _build tmp*    
DJANGO_SETTINGS_MODULE = src.settings.testing
addopts = --reuse-db

testing.py:

from .base import *

DEBUG = True

DATABASES = {
    'default': {
        'ENGINE': 'django.contrib.gis.db.backends.postgis',
        'NAME': 'db',
        'USER': 'root',
        'PASSWORD': 'pass',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

test fixtures:

@pytest.fixture(scope='session')
def user():
    return User.objects.create(name='Test', )

test cases:

import pytest

pytestmark = pytest.mark.django_db


def test_user(user):
    print(user.pk) # returns pk of newly created user
    print(User.objects.all()) # returns queryset with one user


def test_user2(user):
    print(user.pk) # returns the same value as in the previous test 
    print(User.objects.all()) # returns empty queryset

I can't understand behavior of pytest fixtures. Model instance is created once per session and it is the same in several test cases. But actual db value is different. Pytest remove the user value after the first test case.
How can I prevent that behavior and keep my db records saved for all test session?


Solution

  • It's not a problem of --reuse-db since the user is removed from one test to the next within the same test run.

    The problem is you're setting up the fixture by to have a session scope, which means the fixture will be executed once per test run, and since Django will flush the database between tests your User instance is no longer available for the second test. Simply remove the scope from the fixture decorator:

    @pytest.fixture()
    def user():
        return User.objects.create(username='Test')
    

    Edit: From the pytest-django docs "Once setup the database is cached for used for all subsequent tests and rolls back transactions to isolate tests from each other. This is the same way the standard Django TestCase uses the database."

    I don't see why you'd want to use the exact same User instance between tests, even if you were to mutate that particular instance it would mean the tests would depend on each other. In order to be able to isolate tests you should be able to provide the User as the tests expects.