Agent Skill
2/7/2026django-dev-test
This skill should be used when the user asks to "write tests", "django tests", "pytest", "test factories", "create test", "add tests", "test coverage", or mentions testing Django applications, fixtures, or factory_boy. Provides pytest-django patterns with factory_boy for test data generation.
S
sergio
4GitHub Stars
2Views
npx skills add sergio-bershadsky/ai
SKILL.md
| Name | django-dev-test |
| Description | This skill should be used when the user asks to "write tests", "django tests", "pytest", "test factories", "create test", "add tests", "test coverage", or mentions testing Django applications, fixtures, or factory_boy. Provides pytest-django patterns with factory_boy for test data generation. |
name: django-dev-test description: | This skill should be used when the user asks to "write tests", "django tests", "pytest", "test factories", "create test", "add tests", "test coverage", or mentions testing Django applications, fixtures, or factory_boy. Provides pytest-django patterns with factory_boy for test data generation.
Django Testing Patterns
pytest-django testing with factory_boy for fixture management.
Core Principles
- pytest only - Never use Django's TestCase
- factory_boy - Use factories for all test data
- Mirror structure - Tests mirror app structure
- Isolation - Each test fully isolated
- Fast fixtures - Prefer
@pytest.fixtureover setUp
Installation
pip install pytest pytest-django factory-boy pytest-cov
Configuration
pytest.ini or pyproject.toml:
# pyproject.toml
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "config.settings"
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"--strict-markers",
"-ra",
"--tb=short",
]
markers = [
"slow: marks tests as slow",
"integration: marks tests as integration tests",
]
Test Structure
tests/
├── conftest.py # Shared fixtures
├── factories/
│ ├── __init__.py
│ ├── user.py # UserFactory
│ ├── product.py # ProductFactory
│ └── order.py # OrderFactory
├── unit/
│ ├── models/
│ │ ├── test_user.py
│ │ └── test_product.py
│ └── services/
│ └── test_user_service.py
├── integration/
│ └── api/
│ ├── test_users.py
│ └── test_products.py
└── e2e/
└── test_checkout.py
Conftest Setup
tests/conftest.py:
import pytest
from django.test import Client
from ninja.testing import TestClient
from apps.myapp.api import api
@pytest.fixture
def client():
"""Django test client."""
return Client()
@pytest.fixture
def api_client():
"""Django Ninja test client."""
return TestClient(api)
@pytest.fixture
def authenticated_client(api_client, user):
"""API client with authentication."""
api_client.headers["Authorization"] = f"Bearer {user.get_token()}"
return api_client
@pytest.fixture
def user(user_factory):
"""Default test user."""
return user_factory()
@pytest.fixture
def admin_user(user_factory):
"""Admin test user."""
return user_factory(is_staff=True, is_superuser=True)
Factory Pattern
Base factory in factories/__init__.py:
import factory
from factory.django import DjangoModelFactory
class BaseFactory(DjangoModelFactory):
"""Base factory with common patterns."""
class Meta:
abstract = True
@classmethod
def _create(cls, model_class, *args, **kwargs):
"""Override to handle soft-deleted models."""
obj = super()._create(model_class, *args, **kwargs)
return obj
User factory in factories/user.py:
import factory
from factory import fuzzy
from apps.users.models import User
from . import BaseFactory
class UserFactory(BaseFactory):
"""Factory for User model."""
class Meta:
model = User
skip_postgeneration_save = True
email = factory.LazyAttribute(
lambda obj: f"{obj.name.lower().replace(' ', '.')}@example.com"
)
name = factory.Faker("name")
is_active = True
@factory.post_generation
def password(obj, create, extracted, **kwargs):
password = extracted or "testpass123"
obj.set_password(password)
if create:
obj.save(update_fields=["password"])
@classmethod
def _create(cls, model_class, *args, **kwargs):
"""Create with explicit password handling."""
password = kwargs.pop("password", None)
user = super()._create(model_class, *args, **kwargs)
if password:
user.set_password(password)
user.save(update_fields=["password"])
return user
Order factory with relationships (factories/order.py):
import factory
from decimal import Decimal
from apps.orders.models import Order, OrderItem
from . import BaseFactory
from .user import UserFactory
from .product import ProductFactory
class OrderFactory(BaseFactory):
"""Factory for Order model."""
class Meta:
model = Order
user = factory.SubFactory(UserFactory)
status = "pending"
total = factory.LazyAttribute(lambda obj: Decimal("0.00"))
@factory.post_generation
def items(obj, create, extracted, **kwargs):
if not create:
return
if extracted:
for item in extracted:
OrderItemFactory(order=obj, **item)
else:
# Create 1-3 random items
import random
for _ in range(random.randint(1, 3)):
OrderItemFactory(order=obj)
# Update total
obj.total = sum(item.subtotal for item in obj.items.all())
obj.save(update_fields=["total"])
class OrderItemFactory(BaseFactory):
"""Factory for OrderItem model."""
class Meta:
model = OrderItem
order = factory.SubFactory(OrderFactory)
product = factory.SubFactory(ProductFactory)
quantity = factory.fuzzy.FuzzyInteger(1, 5)
unit_price = factory.LazyAttribute(lambda obj: obj.product.price)
Register Fixtures
In conftest.py:
from tests.factories.user import UserFactory
from tests.factories.product import ProductFactory
from tests.factories.order import OrderFactory
@pytest.fixture
def user_factory():
return UserFactory
@pytest.fixture
def product_factory():
return ProductFactory
@pytest.fixture
def order_factory():
return OrderFactory
Test Patterns
Model Tests
# tests/unit/models/test_user.py
import pytest
from apps.users.models import User
@pytest.mark.django_db
class TestUserModel:
def test_create_user(self, user_factory):
user = user_factory(email="test@example.com", name="Test User")
assert user.email == "test@example.com"
assert user.name == "Test User"
assert user.is_active is True
assert user.id is not None
def test_soft_delete(self, user_factory):
user = user_factory()
user.delete()
assert user.is_deleted is True
assert User.objects.filter(id=user.id).exists()
def test_display_name(self, user_factory):
user = user_factory(name="John Doe")
assert user.display_name == "John Doe"
user_no_name = user_factory(name="", email="jane@example.com")
assert user_no_name.display_name == "jane"
Service Tests
# tests/unit/services/test_user_service.py
import pytest
from unittest.mock import patch, MagicMock
from apps.users.services import UserService
@pytest.mark.django_db
class TestUserService:
def test_create_user(self, user_factory):
user = UserService.create(
email="new@example.com",
name="New User",
password="securepass123",
)
assert user.email == "new@example.com"
assert user.check_password("securepass123")
def test_create_user_duplicate_email(self, user_factory):
user_factory(email="existing@example.com")
with pytest.raises(ValueError, match="already registered"):
UserService.create(
email="existing@example.com",
name="Another User",
password="password123",
)
@patch("apps.users.services.email.send_welcome_email")
def test_create_user_sends_email(self, mock_send, user_factory):
user = UserService.create(
email="new@example.com",
name="New User",
password="password123",
)
mock_send.assert_called_once_with(user)
API Tests
# tests/integration/api/test_users.py
import pytest
@pytest.mark.django_db
class TestUsersAPI:
def test_list_users_requires_auth(self, api_client):
response = api_client.get("/users/")
assert response.status_code == 401
def test_list_users(self, authenticated_client, user_factory):
user_factory.create_batch(5)
response = authenticated_client.get("/users/")
assert response.status_code == 200
data = response.json()
assert len(data["items"]) >= 5
def test_create_user(self, authenticated_client):
response = authenticated_client.post("/users/", json={
"email": "new@example.com",
"name": "New User",
"password": "securepass123",
})
assert response.status_code == 201
assert response.json()["email"] == "new@example.com"
def test_get_user(self, authenticated_client, user_factory):
user = user_factory()
response = authenticated_client.get(f"/users/{user.id}")
assert response.status_code == 200
assert response.json()["id"] == str(user.id)
def test_get_user_not_found(self, authenticated_client):
import uuid
fake_id = uuid.uuid4()
response = authenticated_client.get(f"/users/{fake_id}")
assert response.status_code == 404
Fixtures
See references/fixtures.md for advanced fixture patterns including:
- Database transactions
- File uploads
- External service mocking
- Time freezing
Additional Resources
Reference Files
references/fixtures.md- Advanced fixture patterns, mocking, parametrization
Related Skills
- django-dev - Core Django patterns
- django-dev-ninja - API patterns being tested
Skills Info
Original Name:django-dev-testAuthor:sergio
Download