1. Test Setup với 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
Bài tiếp theo: Performance Optimization.