django-tdd/SKILL.md
Django testing strategies with pytest-django, TDD methodology, factory_boy, mocking, coverage, and testing Django REST Framework APIs.
npx skillsauth add lidge-jun/cli-jaw-skills django-tddInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Test-driven development for Django applications using pytest, factory_boy, and Django REST Framework.
# 1. Red — write a failing test
def test_user_creation():
user = User.objects.create_user(email='[email protected]', password='testpass123')
assert user.email == '[email protected]'
assert user.check_password('testpass123')
assert not user.is_staff
# 2. Green — make the test pass (create User model or factory)
# 3. Refactor — improve while keeping tests green
# pytest.ini
[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
# config/settings/test.py
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
# tests/conftest.py
import pytest
from django.contrib.auth import get_user_model
User = get_user_model()
@pytest.fixture(autouse=True)
def timezone_settings(settings):
settings.TIME_ZONE = 'UTC'
@pytest.fixture
def user(db):
return User.objects.create_user(
email='[email protected]',
password='testpass123',
username='testuser'
)
@pytest.fixture
def admin_user(db):
return User.objects.create_superuser(
email='[email protected]',
password='adminpass123',
username='admin'
)
@pytest.fixture
def authenticated_client(client, user):
client.force_login(user)
return client
@pytest.fixture
def api_client():
from rest_framework.test import APIClient
return APIClient()
@pytest.fixture
def authenticated_api_client(api_client, user):
api_client.force_authenticate(user=user)
return api_client
# tests/factories.py
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())
description = factory.Faker('text')
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(' ', '-'))
description = factory.Faker('text')
price = fuzzy.FuzzyDecimal(10.00, 1000.00, 2)
stock = fuzzy.FuzzyInteger(0, 100)
is_active = True
category = factory.SubFactory(CategoryFactory)
created_by = factory.SubFactory(UserFactory)
@factory.post_generation
def tags(self, create, extracted, **kwargs):
if not create or not extracted:
return
for tag in extracted:
self.tags.add(tag)
Use ProductFactory(), ProductFactory.create_batch(10), or pass overrides like ProductFactory(price=100). See references/code-examples.md § Using Factories for full examples.
Test models with factories and @pytest.mark.django_db. Cover creation, string representation, field validation, custom managers, and domain methods.
See references/code-examples.md § Model Testing for complete TestUserModel and TestProductModel examples.
Test Django views using client (anonymous) and authenticated_client fixtures. Verify status codes, context data, redirects, and permission enforcement.
See references/code-examples.md § View Testing for full TestProductViews examples.
Test serializers for serialization, deserialization, and field validation. Test API viewsets for CRUD operations, authentication requirements, filtering, and search.
See references/code-examples.md § DRF Serializer Testing and § DRF API ViewSet Testing for complete examples.
Use @patch to isolate external service calls. Configure return values or side effects on the mock, then assert both response behavior and mock interactions.
from unittest.mock import patch
import pytest
class TestPaymentView:
@patch('apps.payments.services.stripe')
def test_successful_payment(self, mock_stripe, client, user, product):
mock_stripe.Charge.create.return_value = {
'id': 'ch_123', 'status': 'succeeded', 'amount': 9999,
}
client.force_login(user)
response = client.post(reverse('payments:process'), {
'product_id': product.id, 'token': 'tok_visa',
})
assert response.status_code == 302
mock_stripe.Charge.create.assert_called_once()
For email testing with mail.outbox and @override_settings, see references/code-examples.md § Mocking Email.
Test full user flows (register → login → browse → cart → checkout) end-to-end, combining real views with mocked payment services.
See references/code-examples.md § Integration Testing for a complete checkout flow example.
test_user_cannot_delete_others_post--reuse-db and --nomigrationspytest --cov=apps --cov-report=html --cov-report=term-missing
open htmlcov/index.html
| Component | Target Coverage | |-------------|-----------------| | Models | 90%+ | | Serializers | 85%+ | | Views | 80%+ | | Services | 90%+ | | Utilities | 80%+ | | Overall | 80%+ |
| 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 |
Tests are documentation — good tests explain how your code should work. Keep them simple, readable, and maintainable.
development
Native Web UI structured renderer schemas for compose-block drafts, search-results cards, dataframe tables, chart-json charts, and diff output
tools
Unified search hub. Route any web/real-time/X lookup through a 4-tier escalation: built-in web search → cli-jaw browser CDP → progrok Grok OAuth → web-ai (Grok Expert / GPT Pro). Use for: search, 검색, web search, latest news, real-time info, X/Twitter, fact lookup, deep research.
development
UI/UX intent discovery, design vocabulary, product personalities, UX state patterns, typography line break judgment, favicon/product logo design, and logo trust section design. Use when user design direction is vague, when building onboarding/empty/error states, when setting up favicons or product logos, or when referencing a product aesthetic.
development
Canonical owner of module boundary rules, circular dependency detection/prevention, implicit coupling taxonomy, barrel/re-export discipline, and boundary-only defensive programming. Referenced by dev, dev-code-reviewer, dev-backend, dev-frontend stubs.