| name | django-tdd |
| description | Django testing with pytest-django, factory_boy, mocking, and coverage. Use when writing Django tests, doing TDD in Django, testing DRF APIs, or setting up pytest-django and factory_boy. |
| origin | MCC |
Django Testing with TDD
Test-driven development for Django applications using pytest, factory_boy, and Django REST Framework.
When to Activate
- Writing new Django applications
- Implementing Django REST Framework APIs
- Testing Django models, views, and serializers
- Setting up testing infrastructure for Django projects
TDD Workflow for Django
Red-Green-Refactor Cycle
def test_user_creation():
user = User.objects.create_user(email='test@example.com', password='testpass123')
assert user.email == 'test@example.com'
assert user.check_password('testpass123')
assert not user.is_staff
Setup
pytest Configuration
[pytest]
DJANGO_SETTINGS_MODULE = config.settings.test
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts =
--reuse-db
--nomigrations
--cov=apps
--cov-report=html
--cov-report=term-missing
--strict-markers
markers =
slow: marks tests as slow
integration: marks tests as integration tests
Test Settings
from .base import *
DEBUG = True
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
}
}
class DisableMigrations:
def __contains__(self, item):
return True
def __getitem__(self, item):
return None
MIGRATION_MODULES = DisableMigrations()
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.MD5PasswordHasher',
]
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
CELERY_TASK_ALWAYS_EAGER = True
CELERY_TASK_EAGER_PROPAGATES = True
conftest.py
import pytest
from django.contrib.auth import get_user_model
User = get_user_model()
@pytest.fixture
def user(db):
"""Create a test user."""
return User.objects.create_user(
email='test@example.com', password='testpass123', username='testuser'
)
@pytest.fixture
def admin_user(db):
"""Create an admin user."""
return User.objects.create_superuser(
email='admin@example.com', password='adminpass123', username='admin'
)
@pytest.fixture
def authenticated_client(client, user):
"""Return authenticated client."""
client.force_login(user)
return client
@pytest.fixture
def api_client():
"""Return DRF API client."""
from rest_framework.test import APIClient
return APIClient()
@pytest.fixture
def authenticated_api_client(api_client, user):
"""Return authenticated API client."""
api_client.force_authenticate(user=user)
return api_client
Factory Boy
Factory Setup
import factory
from factory import fuzzy
from django.contrib.auth import get_user_model
from apps.products.models import Product, Category
User = get_user_model()
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = User
email = factory.Sequence(lambda n: f"user{n}@example.com")
username = factory.Sequence(lambda n: f"user{n}")
password = factory.PostGenerationMethodCall('set_password', 'testpass123')
first_name = factory.Faker('first_name')
last_name = factory.Faker('last_name')
is_active = True
class CategoryFactory(factory.django.DjangoModelFactory):
class Meta:
model = Category
name = factory.Faker('word')
slug = factory.LazyAttribute(lambda obj: obj.name.lower())
class ProductFactory(factory.django.DjangoModelFactory):
class Meta:
model = Product
name = factory.Faker('sentence', nb_words=3)
slug = factory.LazyAttribute(lambda obj: obj.name.lower().replace(' ', '-'))
price = fuzzy.FuzzyDecimal(10.00, 1000.00, 2)
stock = fuzzy.FuzzyInteger(, )
is_active =
category = factory.SubFactory(CategoryFactory)
created_by = factory.SubFactory(UserFactory)
():
create:
extracted:
tag extracted:
.tags.add(tag)
Using Factories
def test_product_creation():
product = ProductFactory(price=100.00, stock=50)
assert product.price == 100.00
assert product.stock == 50
def test_multiple_products():
products = ProductFactory.create_batch(10)
assert len(products) == 10
Test Templates
For detailed test templates covering models, views, DRF APIs, mocking, and integration testing, see test-templates.md.
Covers:
- Model tests (creation, validation, managers, string representation)
- View tests (list, detail, create, authentication checks)
- DRF serializer tests (serialization, deserialization, validation)
- DRF API viewset tests (CRUD, filtering, searching, permissions)
- Mocking external services (Stripe, email)
- Integration tests (full checkout flow)
Testing Best Practices
DO
- Use factories: Instead of manual object creation
- One assertion per test: Keep tests focused
- Descriptive test names:
test_user_cannot_delete_others_post
- Test edge cases: Empty inputs, None values, boundary conditions
- Mock external services: Don't depend on external APIs
- Use fixtures: Eliminate duplication
- Test permissions: Ensure authorization works
- Keep tests fast: Use
--reuse-db and --nomigrations
DON'T
- Don't test Django internals: Trust Django to work
- Don't test third-party code: Trust libraries to work
- Don't ignore failing tests: All tests must pass
- Don't make tests dependent: Tests should run in any order
- Don't over-mock: Mock only external dependencies
- Don't test private methods: Test public interface
- Don't use production database: Always use test database
Coverage
Coverage Configuration
pytest --cov=apps --cov-report=html --cov-report=term-missing
open htmlcov/index.html
Coverage Goals
| Component | Target Coverage |
|---|
| Models | 90%+ |
| Serializers | 85%+ |
| Views | 80%+ |
| Services | 90%+ |
| Utilities | 80%+ |
| Overall | 80%+ |
Quick Reference
| Pattern | Usage |
|---|
@pytest.mark.django_db | Enable database access |
client | Django test client |
api_client | DRF API client |
factory.create_batch(n) | Create multiple objects |
patch('module.function') | Mock external dependencies |
override_settings | Temporarily change settings |
force_authenticate() | Bypass authentication in tests |
assertRedirects | Check for redirects |
assertTemplateUsed | Verify template usage |
mail.outbox | Check sent emails |
Remember: Tests are documentation. Good tests explain how your code should work. Keep them simple, readable, and maintainable.
Reference Files
- test-templates.md -- Complete test templates for models, views, DRF APIs, mocking, and integration testing