pytest-django-patterns
pytest-django Testing Patterns
TDD Workflow (RED-GREEN-REFACTOR)
Always follow this cycle:
- RED: Write a failing test first that describes desired behavior
- GREEN: Write minimal code to make the test pass
- REFACTOR: Clean up code while keeping tests green
- REPEAT: Never write production code without a failing test
Critical rule: If implementing a feature or fixing a bug, write the test BEFORE touching production code.
Essential pytest-django Patterns
Database Access
- Use
@pytest.mark.django_dbon any test touching the database - Apply to entire module:
pytestmark = pytest.mark.django_db - Transactions roll back automatically after each test
Fixtures for Test Data
Use Factory Boy for models, pytest fixtures for setup:
-
Factories: Create model instances with realistic data (
UserFactory())- Use
factory.Sequence()for unique fields - Use
factory.Faker()for realistic fake data - Use
factory.SubFactory()for foreign keys - Use
@factory.post_generationfor M2M relationships
- Use
-
Fixtures: Setup clients, auth state, or shared resources
clientfixture: Django test client- Create
auth_clientfixture:client.force_login(user)for authenticated requests - Define in
conftest.pyfor reuse across test files
Test Organization
Structure tests to mirror app structure:
tests/
├── apps/
│ └── posts/
│ ├── test_models.py
│ ├── test_views.py
│ └── test_forms.py
├── factories.py
└── conftest.py
Group related tests in classes:
- Name classes
TestComponentName(e.g.,TestPostListView) - Name test methods descriptively:
test_<action>_<expected_outcome> - Use
@pytest.mark.parametrizefor testing multiple scenarios
What to Test
Views
- Status codes: Correct HTTP responses (200, 404, 302)
- Authentication: Authenticated vs anonymous behavior
- Authorization: User can only access their own data
- Context data: Correct objects passed to template
- Side effects: Database changes, emails sent, tasks queued
- HTMX: Check
HTTP_HX_REQUESTheader returns partial template
Forms
- Validation: Valid data passes, invalid data fails with correct errors
- Edge cases: Empty fields, max lengths, unique constraints
- Clean methods: Custom validation logic works
- Save behavior: Objects created/updated correctly
Models
- Methods:
__str__, custom methods return expected values - Managers/QuerySets: Custom filtering works correctly
- Constraints: Database-level validation enforced
- Signals: Pre/post save hooks execute correctly
Celery Tasks
- Mock external calls: Patch HTTP requests, email sending, etc.
- Test logic only: Don't test actual async execution
- Idempotency: Running task multiple times is safe
Django-Specific Testing Patterns
Testing HTMX Responses
Check partial template rendered when HX-Request header present:
- Pass
HTTP_HX_REQUEST="true"to client request - Assert
response.templatescontains partial template name
Testing Permissions
Create authenticated vs anonymous client fixtures:
- Test redirect/403 for unauthorized access
- Test success for authorized access
Testing QuerySets
Verify efficient queries:
- Create test data with factories
- Execute query
- Assert correct objects returned/excluded
- Verify related objects loaded with
select_related()/prefetch_related()
Testing Forms with Model Instances
Pass instance to form for updates:
form = MyForm(data=new_data, instance=existing_obj)- Verify
form.save()updates, doesn't create
Common Patterns
Parametrize multiple scenarios:
Use @pytest.mark.parametrize("input,expected", [...]) for testing various inputs
Mock external services:
Use mocker.patch() to avoid actual HTTP calls, emails, file operations
Check database changes:
- Assert
Model.objects.filter(...).exists()after creation - Assert
Model.objects.count() == expectedfor deletions - Use
refresh_from_db()to verify updates
Test error handling:
- Invalid form data produces correct errors
- Failed operations return error responses
- User sees appropriate error messages
Running Tests
uv run pytest # All tests
uv run pytest -x # Stop on first failure
uv run pytest --lf # Run last failed
uv run pytest -x --lf # Stop first, last failed only
uv run pytest -k "test_name" # Run tests matching pattern
uv run pytest tests/apps/posts/ # Specific directory
uv run pytest --cov=apps # With coverage report
Common Pitfalls
- Forgetting
@pytest.mark.django_db: Results in "Database access not allowed" errors - Not using factories: Creating instances manually is verbose and brittle
- Testing implementation: Test behavior and outcomes, not internal implementation details
- Skipping TDD: Writing tests after code means tests follow implementation, missing edge cases
- Over-mocking: Mock external dependencies, not your own code
- Testing framework code: Don't test Django's ORM, form validation, etc. Test YOUR logic
Setup Requirements
In pyproject.toml:
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "config.settings.test"
python_files = ["test_*.py"]
addopts = ["--reuse-db", "-ra"]
In conftest.py:
Define shared fixtures (auth_client, common factories, etc.)
Integration with Other Skills
- systematic-debugging: When fixing bugs, write failing test first to reproduce
- django-models: Test custom managers, QuerySets, and model methods
- django-forms: Test form validation, clean methods, and save behavior
- celery-patterns: Test task logic with mocked external dependencies
More from kjnez/claude-code-django
htmx-patterns
HTMX patterns for Django including partial templates, hx-* attributes, and dynamic UI without JavaScript. Use when building interactive UI, handling AJAX requests, or creating dynamic components.
31django-templates
Django template patterns including inheritance, partials, tags, and filters. Use when working with templates, creating reusable components, or organizing template structure.
30django-forms
Django form handling patterns including ModelForm, validation, clean methods, and HTMX form submission. Use when building forms, implementing validation, or handling form submission.
16celery-patterns
Celery task patterns including task definition, retry strategies, periodic tasks, and best practices. Use when implementing background tasks, scheduled jobs, or async processing.
15django-models
Django model design patterns emphasizing fat models/thin views, QuerySet optimization, and domain logic encapsulation. Use when designing models, optimizing queries, implementing business logic, or working with the ORM.
14django-extensions
Django-extensions management commands for project introspection, debugging, and development. Use when exploring URLs, models, settings, database schema, running scripts, or profiling performance. Triggers on questions about Django project structure, model fields, URL routes, or requests to run development servers.
12