Chuyển đến nội dung chính

Lesson 17: Testing in Django

Django TestCase, pytest-django. Unit test models, views, serializers. API testing with APIClient. Factory Boy, fixtures, mocking, coverage report.

💻 Programming — Lesson 17 Lesson 17: Testing in Django

Django: From Basics to Advanced

Part 5: Testing, Deployment & Production

xdev.asia

1. Test Setup with pytest-django

pip install pytest pytest-django factory-boy
# pytest.ini
[pytest]
DJANGO_SETTINGS_MODULE = config.settings
python_files = tests.py test_*.py *_tests.py
addopts = --reuse-db -v

2. Factory Boy

import factory
from .models import User, Product, Category

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}@test.com')
    password = factory.PostGenerationMethodCall('set_password', 'testpass123')

class CategoryFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Category
    name = factory.Faker('word')
    slug = factory.LazyAttribute(lambda o: o.name.lower())

class ProductFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Product
    name = factory.Faker('sentence', nb_words=3)
    slug = factory.Sequence(lambda n: f'product-{n}')
    price = factory.Faker('pydecimal', left_digits=3, right_digits=2, positive=True)
    category = factory.SubFactory(CategoryFactory)

3. Unit Test Models

import pytest
from .factories import ProductFactory

@pytest.mark.django_db
class TestProduct:
    def test_create_product(self):
        product = ProductFactory(name='Test Product', price=100)
        assert product.name == 'Test Product'
        assert product.price == 100

    def test_str_representation(self):
        product = ProductFactory(name='Widget')
        assert str(product) == 'Widget'

    def test_slug_unique(self):
        ProductFactory(slug='test')
        with pytest.raises(Exception):
            ProductFactory(slug='test')

4. API Testing

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):
    user = UserFactory()
    api_client.force_authenticate(user=user)
    return api_client

@pytest.mark.django_db
class TestProductAPI:
    def test_list_products(self, auth_client):
        ProductFactory.create_batch(5)
        response = auth_client.get('/api/products/')
        assert response.status_code == status.HTTP_200_OK
        assert len(response.data['results']) == 5

    def test_create_product(self, auth_client):
        category = CategoryFactory()
        data = {'name': 'New', 'slug': 'new', 'price': 99, 'category_id': category.id}
        response = auth_client.post('/api/products/', data)
        assert response.status_code == status.HTTP_201_CREATED

    def test_unauthenticated(self, api_client):
        response = api_client.post('/api/products/', {})
        assert response.status_code == status.HTTP_401_UNAUTHORIZED

5. Mocking

from unittest.mock import patch, MagicMock

@pytest.mark.django_db
class TestEmailNotification:
    @patch('accounts.tasks.send_email_task.delay')
    def test_order_sends_email(self, mock_email):
        order = OrderFactory()
        mock_email.assert_called_once_with(
            order.customer.id,
            'Đơn hàng mới',
            f'Đơn hàng #{order.id} đã được tạo.',
        )
# Chạy tests với coverage
pytest --cov=. --cov-report=html

Next article: Performance Optimization.