Pytesting
    Testing
  
Setup
Installation
pip install pytest pytest-djangoConfiguration (pytest.ini)
[pytest]
DJANGO_SETTINGS_MODULE = your_project.settings
python_files = tests.py test_*.py *_tests.py
filterwarnings =
    ignore::DeprecationWarning
    ignore::django.utils.deprecation.RemovedInDjango50Warningor #### pyprojecrt.toml file
[tool.pytest.ini_options]
django_settings_module = "myproject.settings"  # Replace 'myproject' with your actual project name
python_files = ["tests.py", "test_*.py", "*_tests.py"]
addopts = "--reuse-db --tb=short -p no:warnings"Basic Concepts
Test File Structure
# tests/
#   └── test_views.py
#   └── test_models.py
#   └── test_forms.py
#   └── test_api.py
#   └── conftest.py  # shared fixturesSimple Test Example
def test_homepage_status(client):
    response = client.get('/')
    assert response.status_code == 200Fixtures
Basic Fixtures
import pytest
from django.contrib.auth.models import User
@pytest.fixture
def user_data():
    return {
        'username': 'testuser',
        'password': 'testpass123',
        'email': 'test@example.com'
    }
@pytest.fixture
def user(db, user_data):
    return User.objects.create_user(**user_data)
@pytest.fixture
def admin_user(db):
    return User.objects.create_superuser(
        username='admin',
        password='admin123',
        email='admin@example.com'
    )Factory Boy Integration
import factory
from myapp.models import Profile
class UserFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = User
    
    username = factory.Sequence(lambda n: f'user{n}')
    email = factory.LazyAttribute(lambda o: f'{o.username}@example.com')
class ProfileFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Profile
    
    user = factory.SubFactory(UserFactory)
    bio = factory.Faker('text')
@pytest.fixture
def user_with_profile():
    return ProfileFactory()Database Testing
Basic Model Testing
@pytest.mark.django_db
def test_create_user(user_data):
    user = User.objects.create_user(**user_data)
    assert User.objects.count() == 1
    assert user.username == user_data['username']
@pytest.mark.django_db
def test_profile_creation():
    user = UserFactory()
    profile = ProfileFactory(user=user)
    assert profile.user == userQuery Testing
@pytest.mark.django_db
class TestUserQueries:
    def test_user_filter(self):
        UserFactory.create_batch(3)
        assert User.objects.count() == 3
        assert User.objects.filter(username__startswith='user').count() == 3
    def test_user_order(self):
        users = UserFactory.create_batch(3)
        ordered = User.objects.order_by('-date_joined')
        assert list(ordered) == sorted(users, key=lambda x: x.date_joined, reverse=True)Client Testing
URL Testing
def test_homepage(client):
    response = client.get('/')
    assert response.status_code == 200
    assert 'Welcome' in str(response.content)
def test_protected_view(client, user):
    client.force_login(user)
    response = client.get('/protected/')
    assert response.status_code == 200
def test_post_request(client):
    response = client.post('/submit/', {
        'title': 'Test',
        'content': 'Content'
    })
    assert response.status_code == 302  # redirect after successTemplate Testing
def test_template_rendering(client):
    response = client.get('/profile/')
    assert 'profile.html' in [t.name for t in response.templates]
    assert 'Profile Page' in str(response.content)Authentication Testing
Login Testing
@pytest.mark.django_db
class TestAuth:
    def test_login(self, client, user, user_data):
        response = client.post('/login/', {
            'username': user_data['username'],
            'password': user_data['password']
        })
        assert response.status_code == 302
        assert '_auth_user_id' in client.session
    def test_logout(self, client, user):
        client.force_login(user)
        response = client.get('/logout/')
        assert '_auth_user_id' not in client.sessionPermission Testing
from django.contrib.auth.models import Permission
@pytest.mark.django_db
def test_user_permissions(user):
    permission = Permission.objects.get(codename='add_user')
    user.user_permissions.add(permission)
    assert user.has_perm('auth.add_user')Form Testing
Form Validation
from myapp.forms import UserProfileForm
def test_valid_form():
    form = UserProfileForm(data={
        'name': 'John Doe',
        'email': 'john@example.com',
        'bio': 'Test bio'
    })
    assert form.is_valid()
def test_invalid_form():
    form = UserProfileForm(data={})
    assert not form.is_valid()
    assert 'name' in form.errorsFile Upload Testing
import tempfile
from django.core.files.uploadedfile import SimpleUploadedFile
def test_file_upload(client, user):
    client.force_login(user)
    with tempfile.NamedTemporaryFile() as tmp:
        tmp.write(b'test content')
        tmp.seek(0)
        response = client.post('/upload/', {
            'file': SimpleUploadedFile(tmp.name, tmp.read())
        })
    assert response.status_code == 302API Testing
REST Framework Testing
from rest_framework.test import APIClient
import pytest
@pytest.fixture
def api_client():
    return APIClient()
@pytest.mark.django_db
class TestUserAPI:
    def test_list_users(self, api_client, admin_user):
        api_client.force_authenticate(admin_user)
        response = api_client.get('/api/users/')
        assert response.status_code == 200
        assert len(response.json()) > 0
    def test_create_user(self, api_client, admin_user):
        api_client.force_authenticate(admin_user)
        response = api_client.post('/api/users/', {
            'username': 'newuser',
            'email': 'new@example.com',
            'password': 'secret123'
        })
        assert response.status_code == 201Mocking
Basic Mocking
from unittest.mock import patch
def test_external_api_call():
    with patch('requests.get') as mock_get:
        mock_get.return_value.status_code = 200
        mock_get.return_value.json.return_value = {'data': 'test'}
        # Test your function that uses requests.get
        assert your_function() == expected_result
@patch('myapp.services.external_api.make_request')
def test_service(mock_request):
    mock_request.return_value = {'status': 'success'}
    # Test your serviceEmail Mocking
from django.core import mail
def test_send_email(client):
    response = client.post('/send-email/')
    assert len(mail.outbox) == 1
    assert mail.outbox[0].subject == 'Expected Subject'Best Practices
1. Use Fixtures Effectively
- Keep fixtures focused and small
- Use factory boy for complex object creation
- Share fixtures in conftest.py
2. Test Organization
@pytest.mark.django_db
class TestUser:
    """Group related tests in classes"""
    
    def test_create(self):
        # test user creation
        pass
    
    def test_update(self):
        # test user update
        pass3. Parametrize Tests
@pytest.mark.parametrize('username,expected', [
    ('valid_user', True),
    ('inv@lid', False),
    ('', False),
])
def test_username_validation(username, expected):
    form = UserForm(data={'username': username})
    assert form.is_valid() == expected4. Use Markers
@pytest.mark.slow
def test_expensive_operation():
    # long running test
    pass
# Run with: pytest -m "not slow"5. Debug Tips
def test_with_debug(client):
    response = client.get('/view/')
    import pdb; pdb.set_trace()  # Debug point
    # or use pytest --pdb6. Coverage
pytest --cov=myapp
pytest --cov=myapp --cov-report=html7. Configuration Best Practices
# conftest.py
import pytest
from django.conf import settings
@pytest.fixture(autouse=True)
def media_storage(settings, tmpdir):
    settings.MEDIA_ROOT = tmpdir.strpath
@pytest.fixture
def enable_debug(settings):
    settings.DEBUG = TrueRemember to: - Write tests first (TDD when possible) - Keep tests simple and focused - Use meaningful test names - Test edge cases and error conditions - Use appropriate assertions - Keep test data minimal - Clean up after tests - Use continuous integration
Common Testing Scenarios
1. Testing Signals
@pytest.mark.django_db
def test_profile_signal():
    user = User.objects.create_user(username='test')
    assert hasattr(user, 'profile')
    assert user.profile is not None2. Testing Management Commands
from django.core.management import call_command
from io import StringIO
def test_command_output():
    out = StringIO()
    call_command('my_command', stdout=out)
    assert 'Expected output' in out.getvalue()4. Testing Middlewares
def test_middleware(client):
    response = client.get('/')
    assert response['Custom-Header'] == 'Expected Value'5. Testing Admin
from django.contrib.admin.sites import AdminSite
from myapp.admin import UserAdmin
from myapp.models import User
@pytest.mark.django_db
def test_admin_view(admin_client):
    response = admin_client.get('/admin/myapp/user/')
    assert response.status_code == 200
def test_admin_action():
    site = AdminSite()
    user_admin = UserAdmin(User, site)
    # Test admin actions