How to persist data to DB between tests with pytest-django?
Asked Answered
B

2

6

How can I persist data to DB when using pytest/pytest-django in a test-run of a Django application?

I run pytest with py.test --nomigrations --reuse-db -s and the Postgres DB test_<configured_db_name> is created as expected, however nothing seems to be persisted to DB between tests and at the end of the test run the DB is empty.

import pytest
from django.contrib.auth.models import User


@pytest.mark.django_db(transaction=False)
def test_insert_user():
    user = User.objects.create_user(username="test_user", email="[email protected]", password="test")
    users = User.objects.all()
    assert len(users) > 0

@pytest.mark.django_db(transaction=False)
def test_check_user():
    users = User.objects.all()
    assert len(users) > 0

The first test passes, the second does not making me wonder if anything is persisted to DB at all. According to the pytest-django documentation @pytest.mark.django_db(transaction=False) will not rollback whatever has been affected by the decorated test.

Thank you,

/David

Bawdyhouse answered 25/11, 2015 at 23:34 Comment(7)
This is how test runners work. If you want to use the same data in several tests you should look into fixtures in the pytest documentation.Daimon
You either should use fixtures, or something like factory_boy to have data available between tests.Steamroller
Thank you. I ended up using factory_boy and pytest fixtures.Bawdyhouse
This is strange, pytest-django preserves django_migrations table contents, for example, but for all other tables the data is gone. Don't have time now to look into pytest-django sources - maybe it would be doable to have database filled with data from data migrations (pytest-django does preserve django_migrations content); on the other hand the philosophy is to use db only if needed and to create less stuff in db this way...Moniz
Had same issue BTW. It won't let me edit my comment.Moniz
I have more or less the same problem. I have tried to create a fixture that fill the database, I have passed the fixture as argument to two test view. It works only for the first one.Churchill
See working solution here: https://mcmap.net/q/1777950/-how-to-reuse-database-in-some-tests-using-pytest-djangoRudelson
E
6

Another way of prefilling the database with data for each function is like that:

import pytest

from django.contrib.auth.models import User

@pytest.fixture(scope='module')
def django_db_setup(django_db_setup, django_db_blocker):
    print('setup')
    with django_db_blocker.unblock():
        User.objects.create(username='a')
        assert set(u.username for u in User.objects.all()) == {'a'}

@pytest.mark.django_db
def test1():
    print('test1')
    User.objects.create(username='b')
    assert set(u.username for u in User.objects.all()) == {'a', 'b'}

@pytest.mark.django_db
def test2():
    print('test2')
    User.objects.create(username='c')
    assert set(u.username for u in User.objects.all()) == {'a', 'c'}

The good thing about this method is that the setup function is only called once:

plugins: django-3.1.2
collected 2 items

mytest.py setup
test1
.test2
.
=================== 2 passed in 1.38 seconds ====================

The bad thing is that 1.38 seconds is a bit too much for such a simple test. --reuse-db is a faster way to do it.

Expostulation answered 11/5, 2017 at 12:8 Comment(1)
It might be interesting for @Moniz as well.Expostulation
C
0

I have solved this problem -- prefill the DB for every function -- by defining a fixture with scope function (i.e. model and session will not work).

Here is the code for testing the views in Django.

# This is used to fill the database more easily
from mixer.backend.django import mixer

import pytest

from django.test import RequestFactory

from inventory import views
from inventory import services

pytestmark = pytest.mark.django_db

@pytest.fixture(scope="function")
def fill_db():
    """ Just filling the DB with my data """
    for elem in services.Search().get_lookup_data():
        mixer.blend('inventory.Enumeration', **elem)

def test_grid_anonymous(fill_db):
    request = RequestFactory().get('/grid/')
    response = views.items_grid(request)
    assert response.status_code == 200, \
        "Should be callable by anyone"

def test_list_anonymous(fill_db):
    request = RequestFactory().get('/')
    response = views.items_list(request)
    assert response.status_code == 200, \
        "Should be callable by anyone"
Churchill answered 2/3, 2017 at 18:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.