pytest-django-patterns
npx skills add https://github.com/kjnez/claude-code-django --skill pytest-django-patterns
Agent 安装分布
Skill 文档
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