Skip to content
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

setup examples #33

Closed
aljosa opened this issue Apr 12, 2013 · 8 comments
Closed

setup examples #33

aljosa opened this issue Apr 12, 2013 · 8 comments

Comments

@aljosa
Copy link

aljosa commented Apr 12, 2013

i've tried to find examples on how to use new fixture mark w/ pytest_django.
something like:

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

@pytest.fixture(scope="session", autouse=True)
@pytest.mark.django_db
def project_setup(*args, **kwargs):
    user = User.objects.create(username="session user #1")
    assert User.objects.count(), 1
    site, created = Site.objects.get_or_create(id=1)
    site.domain = "testserver"
    site.save()

to do some db modifications after test db is ready.
this fails w/ error "Failed: Database access not allowed, use the "django_db" mark to enable"

could somebody provide an example on howto setup additional stuff after test db is ready?

@pelme
Copy link
Member

pelme commented Apr 12, 2013

This clearly needs to be documented better with examples, I will try to improve the docs.

In the meantime, I hope this can help you:

  1. Fixtures which deals with database modifications must use the default scope of 'function' - the database will be reset after each test.

  2. You can request the db fixture for your own fixture. Practically this means:

@pytest.fixture(autouse=True)
def project_setup(db):  # the db argument will make sure the database access is requested for this fixture
    User.objects.create(username="foo")

You might have something specific in mind, but I just want to give you a warning: I would generally advice against automatically requesting database access for all your tests. You will not be able to turn this of later! You will simply not be able to write unit tests which does not use the database. Your tests will be slow! Global test setup like this also tends to grow over time - it slows tests down and become very hard to maintain in the long run.

(I suffer badly from this in my current project, I have a bunch of "legacy" tests that uses global setup, that are really slow and they break all the time.)

I've only told you what to not do yet. Here is what you can do instead. :-) A solution would be to use factory boy to help creating objects for tests. It makes it easy to get exactly the objects you need for a particular test, without having to resort to global setup. It helps making complicated test setup explicit, easy and at the same time avoiding global fixtures.

Hope this helps!

@aljosa
Copy link
Author

aljosa commented May 16, 2013

is this the best way to test django project/app w/ different settings?

import pytest
from django.conf import settings

@pytest.fixture
def basic_settings():
    settings... # change something in settings

@pytest.fixture
def other_settings():
    settings... # change something in settings

def test_basic(basic_settings):
    pass

def test_other(other_settings):
    pass

@pelme
Copy link
Member

pelme commented May 17, 2013

There is the settings fixture which can be used to automatically restore settings:

@pytest.fixture
def basic_settings(settings):
    settings.SOME_SETTING = 1234
    # SOME_SETTING will be automatically restored

This only works for some settings. Certain settings have side effects that cannot really be undone because of how they are used within Django and other applications. An example of this is INSTALLED_APPS - once the apps has been loaded, there is not really any way to change it.

So depending on what settings you aim to change - this might or might not work, but that is just how Django works.

Unfortunately the settings fixture is not yet documented, I've opened #36 to not forget it.

@pelme pelme closed this as completed in 38dae9e Jul 6, 2013
pelme added a commit that referenced this issue Jul 6, 2013
@pelme pelme reopened this Jul 6, 2013
@ThomasWaldmann
Copy link

if that would be in the docs, it would safe people hours of failure until they find this issue here. :)

@pytest.fixture(autouse=True)  # only works with function scope (default)
def project_setup(db):  # the db argument will make sure the database access is requested for this fixture
    User.objects.create(username="foo")

@pelme
Copy link
Member

pelme commented Dec 26, 2014

@ThomasWaldmann

It would be great to have that example in the documentation.

Maybe there should be a page in the documentation with examples/common patterns? I think examples like this would fit nicely.

A pull request that adds this example to the docs would be great!

@or
Copy link

or commented Sep 3, 2015

This is quite old, but I had a similar problem and found a work-around that worked for me, so in case someone else stumbles across this:

@pytest.fixture(autouse=True, scope='session')
def post_db_setup(_django_db_setup, _django_cursor_wrapper):
    with _django_cursor_wrapper:
        call_command('loaddata', *settings.STATIC_FIXTURES, interactive=False)

I realize fixtures are not the preferred way to do things, but if you really need to fill your DB with some values before any of the tests run... and only once, then that works quite well. It makes sure _django_db_setup has been done and you get _django_cursor_wrapper to write to it (I suspect any model creation would work in that context, but haven't tried it).

@mariocesar
Copy link

mariocesar commented Sep 4, 2017

Have exactly the same problem, kind of frustrating. Will be great if this is documented, it's really a common pattern to populate a DB before running tests, and later cleanup files, attachments, logs, etc. :)

The current updated solution for pytest==3.0.7 pytest-django==3.1.2

@pytest.fixture(scope='session')
def populate_nodes(request, django_db_setup, django_db_blocker):
    from project.app.models import Attachment
    call_command('populate', verbosity=0, interactive=False)

    files = list(Attachment.objects.all().values_list('file', flat=True))
    django_db_blocker.unblock()

    # Cleanup
    def teardown():
        for file_path in files:
            path, ext = os.path.splitext(file_path)
            for path in glob.glob(f'{path}*'):
                os.unlink(path)

        django_db_blocker.restore()

    request.addfinalizer(teardown)
    return request

@pelme
Copy link
Member

pelme commented Sep 6, 2017

@mariocesar Did you see this example in the docs?

https://pytest-django.readthedocs.io/en/latest/database.html#populate-the-database-with-initial-test-data

I'm closing the issue for now (it was supposed to be closed when that example was added). Please reopen with additional details or open another issue if this needs to be further clarified or improved!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants