Testing Basics

pytest-django Quick Start

pytest-django is a plugin for pytest that provides a set of useful tools for testing Django applications and projects. For more information see the pytest-django documentation.

To install pytest-django:

$ pip install -U pytest-django
...

The DJANGO_SETTINGS_MODULE environment variable must be defined and should point to the django settings module used for testing. There are different approaches towards configuration.

  1. Use pytest.ini to define DJANGO_SETTINGS_MODULE variable:

    # -- FILE: pytest.ini (or tox.ini)
    [pytest]
    DJANGO_SETTINGS_MODULE = myapp.tests.settings
    # -- recommended but optional:
    python_files = tests.py test_*.py *_tests.py
    
  2. Set DJANGO_SETTINGS_MODULE environment variable, e.g. in bash or Windows Command prompt

    $ export DJANGO_SETTINGS_MODULE = myapp.tests.settings
    $ pytest
    
  3. Use pytest_configure hook

    # -- FILE: conftest.py
    import os
    from pathlib import Path
    import sys
    import django
    
    TESTS_DIR = Path(__file__).resolve().parent
    BASE_DIR = TESTS_DIR.parent
    sys.path.insert(0, BASE_DIR.absolute())
    
    def pytest_configure():
        os.environ['DJANGO_SETTINGS_MODULE'] = 'myapp.tests.settings'
        django.setup()
    

    See also pytest_configure hook

  4. Use --ds=SETTING command line option

    $ pytest --ds=myapp.tests.settings
    

    Variant to this method is to add the command line option to pytest.ini:

    addopts = --ds=myapp.tests.settings
    

How the setting is choosen? The precedence (from higher to lower) is as follows:

  • The command line option --ds

  • The command line option --ds from pytest.ini or tox.ini

  • The environment variable DJANGO_SETTINGS_MODULE

  • The DJANGO_SETTINGS_MODULE option in the configuration file - pytest.ini (tox.ini)

To override settings you can use the setting fixutre.

@pytest.fixture(autouse=True)
def use_dummy_cache_backend(settings):
    settings.CACHES = {
        "default": {
            "BACKEND": "django.core.cache.backends.dummy.DummyCache",
        }
    }

settings module for testing

import os

# Define environment variables for testing
os.environ['DJANGO_SECRET_KEY'] = 'secret-key'

# Get everything from production settings module
from myproj.settings import *

# Use in-memory database for better testing performance
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': ':memory:',
    }
}

Testing with database

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, transactional_db or django_db_reset_sequences fixtures. Otherwise the test will fail when trying to access the database.

pytest.mark.django_db is used to mark a test function as requiring the database. It will ensure the database is set up 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 TestCase class.

@pytest.mark.django_db
def test_user_model():
    user = User(username='joe')
    assert str(user) == user

Testing with client

Use client fixture - An instance of a django.test.Client.

def test_with_client(client):
    response = client.get('/')
    assert response.status_code == 200

For situations where logged in user is required, following are useful:

  • admin_user fixture - An instance of a superuser, with username “admin” and password “password” (in case there is no “admin” user yet).

  • admin_client fixture - An instance of a django.test.Client, logged in as an admin user.

  • client.force_log() method

def test_authenticated(client, admin_user):
    client.force_login(admin_user)
    response = client.get('/admin/')
    assert response.status_code == 200

def test_authenticated(admin_client):
    response = client.get('/admin/')
    assert response.status_code == 200

Further information: