DEV Community

Ajit Kumar
Ajit Kumar

Posted on

Introducing Automated Testing Late in a Django + DRF Project: Setup, Requirements, and Best Practices - Part-1

Many Django REST Framework projects begin with rapid feature development and no automated test suite. At first this feels productive, but as foreign keys grow, serializers become nested, and the system gains complexity, manual testing becomes fragile and extremely time-consuming.

This two-part series shows exactly how to introduce pytest into an existing Django REST Framework project, even if you’re deep into development.

This first part covers:

  • Why pytest is ideal for Django + DRF
  • Installing pytest and configuring project-level settings
  • Creating a separate test settings file
  • Setting up a dedicated PostgreSQL test database
  • Building fixtures for API clients and test users
  • Handling custom user models safely
  • Preparing your project to run tests reliably

Part 2 will show real-world test cases using a Blog → Writer → Category → SocialMediaMeta use case.

Let’s begin.


1. Why Add Tests at a Later Stage?

Many teams postpone testing until:

  • Models are stabilized
  • Views and serializers begin accumulating logic
  • Manual testing becomes slow or error-prone
  • Refactoring becomes risky
  • Bugs begin recurring
  • Onboarding new developers becomes cumbersome

Good news: pytest integrates cleanly even in a large, fully active Django project, as long as you separate your test configuration correctly.


2. Install pytest and Required Plugins

Install the packages:

pip install pytest pytest-django pytest-cov Faker
Enter fullscreen mode Exit fullscreen mode

These tools give:

  • pytest — fast, expressive testing
  • pytest-django — Django ORM, fixtures, DB setup
  • pytest-cov — code coverage
  • Faker — generating random test data

3. Create pytest.ini in Project Root

[pytest]
DJANGO_SETTINGS_MODULE = project.settings.test
python_files = tests.py test_*.py *_tests.py
addopts = --reuse-db -x
Enter fullscreen mode Exit fullscreen mode

Key points:

  • You must explicitly point pytest to a test settings file.
  • --reuse-db speeds testing dramatically.
  • -x stops after the first failure (optional but useful early on).

4. Create settings/test.py

Instead of reusing your local or production settings, create a lightweight test settings file:

from .local import *

DATABASES["default"]["NAME"] = "test_db"
AUTH_PASSWORD_VALIDATORS = []

TEST_USER = {
    "id": 999,
    "email": "testuser@example.com",
}
Enter fullscreen mode Exit fullscreen mode

Important design decisions:

(a) Separate database

Tests must not touch your development DB.

(b) Disable password validators

Tests should not fail due to password complexity requirements.

(c) TEST_USER

This allows us to bypass complex signup flows (OTP, email verification, custom user manager logic).


5. PostgreSQL: Create a Dedicated Test Database

If you use PostgreSQL (most common in real DRF apps), create a test DB manually:

CREATE DATABASE test_db;
GRANT ALL PRIVILEGES ON DATABASE test_db TO your_db_user;
ALTER DATABASE test_db OWNER TO your_db_user;
Enter fullscreen mode Exit fullscreen mode

This avoids the dreaded:

permission denied to create database
Enter fullscreen mode Exit fullscreen mode

errors.


6. Add tests/ Directory Structure

Follow this recommended structure:

tests/
│── __init__.py
│── conftest.py
│── test_blog_creation.py
│── test_blog_listing.py
│── test_nested_blog_response.py
│── test_signals_blog_metadata.py
Enter fullscreen mode Exit fullscreen mode

The key file here is conftest.py.


7. Create conftest.py with Critical Fixtures

Every DRF project needs these fixtures:

  • An authenticated API client
  • A test user
  • Fixtures for foreign-key related models (Writer, Category, etc.)

Example:

import pytest
from django.contrib.auth import get_user_model
from blogs.models import Writer, Category


@pytest.fixture
def test_user(db, settings):
    User = get_user_model()
    data = settings.TEST_USER

    return User.objects.create(
        id=data["id"],
        email=data["email"],
        is_active=True,
    )


@pytest.fixture
def api_client(test_user):
    from rest_framework.test import APIClient
    client = APIClient()
    client.force_authenticate(test_user)
    return client


@pytest.fixture
def writer(db):
    return Writer.objects.create(
        name="Jane Writer",
        email="jane@example.com",
    )


@pytest.fixture
def category(db):
    return Category.objects.create(
        title="Technology",
        slug="tech",
    )
Enter fullscreen mode Exit fullscreen mode

This ensures:

  • No login/OTP/verification steps
  • Reliable FK dependencies
  • Authenticated API requests automatically

8. Running Tests for the First Time

pytest -s
Enter fullscreen mode Exit fullscreen mode

If everything is configured correctly, you’ll see the test database created, and pytest will run successfully.

Part 2 will introduce complete DRF test cases using realistic model relationships.

Next in Part 2: Real-World DRF Testing Use Cases

In the next section, we will cover:

  • Blog + Writer + Category + SocialMediaMeta sample models
  • Nested serializers
  • Foreign key creation tests
  • Signals
  • Reverse relationships
  • Error validation tests
  • Testing nested JSON structures
  • Testing authenticated requests

Top comments (0)