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: .. code-block:: console $ 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: .. code-block:: ini # -- 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 .. code-block:: console $ export DJANGO_SETTINGS_MODULE = myapp.tests.settings $ pytest 3. Use ``pytest_configure`` hook .. code-block:: python # -- 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 .. code-block:: console $ pytest --ds=myapp.tests.settings Variant to this method is to add the command line option to ``pytest.ini``: .. code-block:: 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. .. code-block:: python @pytest.fixture(autouse=True) def use_dummy_cache_backend(settings): settings.CACHES = { "default": { "BACKEND": "django.core.cache.backends.dummy.DummyCache", } } ``settings`` module for testing ------------------------------- .. code-block:: python 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. .. code-block:: python @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 `_. .. code-block:: python 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 .. code-block:: python 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: - `Client class `_ - `Response class `_