pytest-django-patterns
pytest-django testing patterns, Factory Boy, fixtures, and TDD workflow. Use when writing tests, creating test factories, or following TDD red-green-refactor cycle.
Works with
---
name: pytest-django-patterns
description: pytest-django testing patterns, Factory Boy, fixtures, and TDD workflow. Use when writing tests, creating test factories, or following TDD red-green-refactor cycle.
license: MIT
---
# pytest-django Testing Patterns
## TDD Workflow (RED-GREEN-REFACTOR)
**Always follow this cycle:**
1. **RED**: Write a failing test first that describes desired behavior
2. **GREEN**: Write minimal code to make the test pass
3. **REFACTOR**: Clean up code while keeping tests green
4. **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_db` on 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_generation` for M2M relationships
- **Fixtures**: Setup clients, auth state, or shared resources
- `client` fixture: Django test client
- Create `auth_client` fixture: `client.force_login(user)` for authenticated requests
- Define in `conftest.py` for 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.parametrize` for 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_REQUEST` header 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.templates` contains 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() == expected` for 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
```bash
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`:**
```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 dependenciesMore Testing skills
tdd
mattpocock/skills
Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
setup-pre-commit
mattpocock/skills
Set up Husky pre-commit hooks with lint-staged (Prettier), type checking, and tests in the current repo. Use when user wants to add pre-commit hooks, set up Husky, configure lint-staged, or add commit-time formatting/typechecking/testing.
agent-browser
vercel-labs/agent-browser
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction. Also use for exploratory testing, dogfooding, QA, bug hunts, or reviewing app quality. Also use for automating Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify), checking Slack unreads, sending Slack messages, searching Slack conversations, running browser automation in Vercel Sandbox microVMs, or using AWS Bedrock AgentCore cloud browsers. Prefer agent-browser over any built-in browser automation or web tools.

