一、测试环境搭建

1. 配置测试设置

# settings_test.py (继承主配置)  
from .settings import *  

# 使用内存数据库加速  
DATABASES = {  
    'default': {  
        'ENGINE': 'django.db.backends.sqlite3',  
        'NAME': ':memory:'  # 内存数据库  
    }  
}  

# 禁用密码哈希加速  
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]  

# 关闭调试模式  
DEBUG = False  
TEMPLATE_DEBUG = False  

2. 核心依赖安装

pip install pytest pytest-django pytest-mock coverage  

二、测试文件结构规范

myapp/  
├── tests/  
│   ├── __init__.py  
│   ├── test_models.py  
│   ├── test_views.py  
│   ├── test_forms.py  
│   ├── test_utils/  
│   │   ├── test_validators.py  
│   ├── factories.py  # 测试数据工厂  
├── models.py  
└── views.py  

三、五大核心测试类型

1. 模型测试 (test_models.py)

from django.test import TestCase  
from .models import Product  
from .factories import ProductFactory  

class ProductModelTest(TestCase):  
    @classmethod  
    def setUpTestData(cls):  
        # 创建一次供所有测试使用  
        cls.product = ProductFactory(name="Test Product")  

    def test_name_label(self):  
        field_label = self.product._meta.get_field('name').verbose_name  
        self.assertEqual(field_label, 'product name')  

    def test_price_max_digits(self):  
        max_digits = self.product._meta.get_field('price').max_digits  
        self.assertEqual(max_digits, 10)  

    def test_get_absolute_url(self):  
        url = self.product.get_absolute_url()  
        self.assertEqual(url, f'/products/{self.product.id}/')  

2. 视图测试 (test_views.py)

from django.urls import reverse  
from rest_framework.test import APITestCase  

class ProductViewTest(APITestCase):  
    def setUp(self):  
        self.product = ProductFactory()  
        self.list_url = reverse('product-list')  
        self.detail_url = reverse('product-detail', args=[self.product.id])  

    def test_list_view_status_code(self):  
        response = self.client.get(self.list_url)  
        self.assertEqual(response.status_code, 200)  

    def test_detail_view_content(self):  
        response = self.client.get(self.detail_url)  
        self.assertContains(response, self.product.name)  

    def test_unauthorized_create(self):  
        data = {'name': 'New', 'price': 99.9}  
        response = self.client.post(self.list_url, data)  
        self.assertEqual(response.status_code, 401)  

3. 表单测试 (test_forms.py)

from django.test import TestCase  
from .forms import ProductForm  

class ProductFormTest(TestCase):  
    def test_valid_data(self):  
        form = ProductForm(data={  
            'name': 'Valid Product',  
            'price': 50.00,  
            'category': 1  
        })  
        self.assertTrue(form.is_valid())  

    def test_invalid_price(self):  
        form = ProductForm(data={'name': 'Test', 'price': -10})  
        self.assertFalse(form.is_valid())  
        self.assertIn('price', form.errors)  

4. API测试 (DRF)

from rest_framework import status  
from .factories import UserFactory  

class ProductAPITest(APITestCase):  
    def setUp(self):  
        self.user = UserFactory()  
        self.client.force_login(self.user)  

    def test_create_product(self):  
        data = {"name": "API Product", "price": 199.9}  
        response = self.client.post('/api/products/', data, format='json')  
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)  
        self.assertEqual(response.data['name'], "API Product")  

5. 中间件/工具测试

from django.test import RequestFactory  
from .middleware import CustomMiddleware  

class MiddlewareTest(TestCase):  
    def test_custom_header(self):  
        factory = RequestFactory()  
        request = factory.get('/')  
        middleware = CustomMiddleware(lambda req: None)  
        middleware(request)  
        self.assertIn('X-Custom-Header', request.META)  

四、高效测试数据管理

1. 使用Factories (factory_boy)

# factories.py  
import factory  
from .models import Product, User  

class UserFactory(factory.django.DjangoModelFactory):  
    class Meta:  
        model = User  

    username = factory.Sequence(lambda n: f'user_{n}')  
    email = factory.LazyAttribute(lambda u: f'{u.username}@example.com')  
    is_active = True  

class ProductFactory(factory.django.DjangoModelFactory):  
    class Meta:  
        model = Product  

    name = factory.Faker('word')  
    price = factory.Faker('pydecimal', left_digits=3, right_digits=2, positive=True)  
    owner = factory.SubFactory(UserFactory)  

2. Fixtures 高级用法

# products.json  
[  
  {  
    "model": "shop.Product",  
    "pk": 1,  
    "fields": {  
      "name": "Fixture Product",  
      "price": "99.99"  
    }  
  }  
]  
class TestWithFixtures(TestCase):  
    fixtures = ['products.json']  # 自动加载  
  
    def test_fixture_data(self):  
        product = Product.objects.get(pk=1)  
        self.assertEqual(product.price, Decimal('99.99'))  

五、Mock 外部依赖

1. 模拟第三方 API

from unittest.mock import patch  
from .services import PaymentService  

class PaymentTest(TestCase):  
    @patch('myapp.services.requests.post')  # 模拟requests  
    def test_payment_success(self, mock_post):  
        # 配置模拟响应  
        mock_post.return_value.status_code = 200  
        mock_post.return_value.json.return_value = {"status": "success"}  

        result = PaymentService.process(amount=100)  
        self.assertTrue(result.is_success)  

2. 模拟时间

from freezegun import freeze_time  

class TimeSensitiveTest(TestCase):  
    @freeze_time("2023-01-01")  
    def test_new_year(self):  
        from datetime import date  
        self.assertEqual(date.today(), date(2023, 1, 1))  

六、测试覆盖率优化

1. 配置 coverage.py

# .coveragerc  
[run]  
source =  
    myapp/  
omit =  
    */migrations/*  
    */tests/*  
    */__init__.py  

[report]  
show_missing = true  
fail_under = 85  # 低于85%失败  

2. 执行测试并生成报告

coverage run -m pytest  
coverage html  # 生成HTML报告  

七、测试运行优化

1. 并行测试

pytest -n auto  # 自动使用所有CPU核心  

2. 只运行失败用例

pytest --lf  # 上次失败的用例  
pytest --ff  # 先运行失败用例  

3. 标记慢测试

@pytest.mark.slow  
def test_slow_operation():  
    time.sleep(5)  
pytest -m "not slow"  # 跳过慢测试  

八、测试最佳实践

1. 测试金字塔策略

70%20%10%测试类型分布单元测试集成测试端到端测试

2. 核心原则

原则说明
FIRST原则Fast, Isolated, Repeatable, Self-Validating, Timely
Red-Green-Refactor测试驱动开发流程
最小化数据库交互使用mock和内存数据库加速
单断言原则每个测试验证一个行为

3. 常见缺陷规避

# 错误示范:  
- 测试间存在依赖关系  
- 包含业务逻辑的测试数据  
- 过度测试实现细节  

# 正确做法:  
+ 每个测试独立可运行  
+ 使用工厂创建测试数据  
+ 关注接口行为而非内部实现  

九、Django 测试框架对比

特性django.testpytest
用例组织Class-based函数式+Class
参数化测试需第三方库内置支持
Fixture 管理Fixtures内置+插件
Mock 支持unittest.mock内置monkeypatch
插件生态有限丰富
执行速度较慢快(并行)

推荐组合:

pytest + pytest-django + factory_boy + coverage  

完整示例项目:Django Testing Example
持续集成配置:GitHub Actions for Django

通过本指南,您将能够为Django应用构建完备的测试防护网,覆盖从模型到API的全栈功能,大幅提升代码质量和交付信心!

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐