L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Données et recherche · SKILL.md

pytest-django

pytest-django

pytest-django integration testing specialist. Covers all fixtures (db, transactional_db, client, rf, settings, mailoutbox, django_user_model), @pytest.mark.django_db options, DRF APIClient, factory_boy integration, async views, signals, management commands, and multi-database testing. USE WHEN: user mentions "pytest-django", "django test", "@pytest.mark.dj…

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: pytest-django description: | pytest-django integration testing specialist. Covers all fixtures (db, transactional_db, client, rf, settings, mailoutbox, django_user_model), @pytest.mark.django_db options, DRF APIClient, factory_boy integration, async views, signals, management commands, and multi-database testing.

USE WHEN: user mentions "pytest-django", "django test", "@pytest.mark.django_db", asks about "django client fixture", "DRF APIClient", "django signals test", "management command test", "django async view test".

DO NOT USE FOR: Non-Django Python tests - use pytest or python-integration; FastAPI - use fastapi-testing; Pure container setup - use testcontainers-python allowed-tools: Read, Grep, Glob, Write, Edit

pytest-django

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: pytest-django for comprehensive documentation on all fixtures, markers, and advanced patterns.

Setup

# pyproject.toml
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "myproject.settings.test"
python_files = ["tests.py", "test_*.py", "*_tests.py"]

@pytest.mark.django_db Options

@pytest.mark.django_db                               # basic DB access, rollback
@pytest.mark.django_db(transaction=True)             # real commits (TransactionTestCase)
@pytest.mark.django_db(transaction=True, reset_sequences=True)  # reset auto-increment
@pytest.mark.django_db(databases=["default", "analytics"])      # multiple DBs
@pytest.mark.django_db(databases="__all__")          # all databases
@pytest.mark.django_db(serialized_rollback=True)     # for data migration tests

Core Fixtures

def test_basic(db):               # DB access fixture (use in other fixtures)
    pass

def test_client(client):          # django.test.Client
    response = client.get("/")
    assert response.status_code == 200

def test_admin(admin_client):     # pre-logged-in superuser client
    response = admin_client.get("/admin/")
    assert response.status_code == 200

def test_rf(rf, admin_user):      # RequestFactory (bypasses middleware)
    request = rf.get("/items/")
    request.user = admin_user
    response = my_view(request)

def test_settings(settings):      # modify settings, auto-reverted
    settings.DEBUG = True
    settings.CACHES = {"default": {"BACKEND": "...DummyCache"}}

def test_mail(mailoutbox):        # access sent emails
    send_mail("Subject", "Body", "from@x.com", ["to@x.com"])
    assert len(mailoutbox) == 1
    assert mailoutbox[0].subject == "Subject"

def test_user(django_user_model): # AUTH_USER_MODEL
    user = django_user_model.objects.create_user("alice", password="pass")
    assert user.check_password("pass")

Authentication Patterns

def test_force_login(client, django_user_model):
    user = django_user_model.objects.create_user("alice", password="pass")
    client.force_login(user)            # fastest, no password check
    response = client.get("/private/")
    assert response.status_code == 200

def test_client_login(client, django_user_model):
    django_user_model.objects.create_user("alice", password="pass")
    client.login(username="alice", password="pass")
    response = client.get("/private/")
    assert response.status_code == 200

DRF APIClient

import pytest
from rest_framework.test import APIClient
from rest_framework import status

@pytest.fixture
def api_client():
    return APIClient()

@pytest.fixture
def auth_client(api_client, django_user_model, db):
    user = django_user_model.objects.create_user("alice", password="pass")
    api_client.force_authenticate(user=user)
    return api_client

@pytest.mark.django_db
def test_create(auth_client):
    resp = auth_client.post("/api/items/", {"name": "Widget"}, format="json")
    assert resp.status_code == status.HTTP_201_CREATED
    assert resp.data["name"] == "Widget"

@pytest.mark.django_db
def test_token_auth(api_client, django_user_model):
    from rest_framework.authtoken.models import Token
    user = django_user_model.objects.create_user("alice", password="pass")
    token = Token.objects.create(user=user)
    api_client.credentials(HTTP_AUTHORIZATION="Token " + token.key)
    resp = api_client.get("/api/me/")
    assert resp.status_code == status.HTTP_200_OK

factory_boy Integration

# factories.py
from factory.django import DjangoModelFactory
import factory

class UserFactory(DjangoModelFactory):
    class Meta:
        model = "auth.User"
        django_get_or_create = ("username",)

    username = factory.Sequence(lambda n: f"user{n}")
    email    = factory.LazyAttribute(lambda o: f"{o.username}@example.com")
    password = factory.django.Password("testpass123")

class ArticleFactory(DjangoModelFactory):
    class Meta:
        model = "myapp.Article"

    title  = factory.Sequence(lambda n: f"Article {n}")
    author = factory.SubFactory(UserFactory)
    body   = factory.Faker("paragraph")

# conftest.py
@pytest.fixture
def user(db):
    return UserFactory()

@pytest.fixture
def article(db):
    return ArticleFactory()

Testing Signals

from unittest.mock import MagicMock
from django.db.models.signals import post_save

@pytest.mark.django_db
def test_signal_fires(db):
    handler = MagicMock()
    post_save.connect(handler, sender=Article)
    try:
        Article.objects.create(title="Test", body="Content")
        assert handler.called
        assert handler.call_args[1]["created"] is True
    finally:
        post_save.disconnect(handler, sender=Article)

Testing Management Commands

from io import StringIO
from django.core.management import call_command

@pytest.mark.django_db
def test_command_output():
    out = StringIO()
    call_command("my_command", stdout=out, verbosity=2)
    assert "Success" in out.getvalue()

Async Views (Django 4.1+)

@pytest.mark.asyncio
@pytest.mark.django_db(transaction=True)
async def test_async_view(async_client):
    response = await async_client.get("/async-endpoint/")
    assert response.status_code == 200

Query Count Assertions

def test_no_n_plus_one(client, django_assert_max_num_queries):
    with django_assert_max_num_queries(5):
        response = client.get("/api/articles/")
    assert response.status_code == 200

def test_exact_queries(django_assert_num_queries):
    with django_assert_num_queries(1):
        Article.objects.get(pk=1)

on_commit Callbacks

def test_email_on_commit(client, mailoutbox, django_capture_on_commit_callbacks):
    with django_capture_on_commit_callbacks(execute=True):
        client.post("/orders/", {"item_id": 1})
    assert len(mailoutbox) == 1

Recommended conftest.py

# conftest.py
import pytest

@pytest.fixture(autouse=True)
def fast_password_hasher(settings):
    settings.PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]

@pytest.fixture(autouse=True)
def dummy_cache(settings):
    settings.CACHES = {"default": {"BACKEND": "django.core.cache.backends.dummy.DummyCache"}}

@pytest.fixture(autouse=True)
def email_backend(settings):
    settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"

Anti-Patterns

Anti-PatternSolution
transaction=True for everythingUse default (rollback) unless you need real commits
db in session fixtureUse django_db_blocker for session scope
Not resetting factory sequencesUserFactory.reset_sequence(0) in fixtures
Not using force_loginPrefer force_login over login for speed

Official docs: https://pytest-django.readthedocs.io/

Skills enregistrées