博客
关于我
# Django 单元测试完全指南 —— 从基础到高级的测试框架实战
阅读量:797 次
发布时间:2023-04-04

本文共 6067 字,大约阅读时间需要 20 分钟。

Django 测试实践指南

测试环境搭建

配置测试设置

# settings_test.py (继承主配置)from .settings import *# 使用内存数据库加速DATABASES = {    'default': {        'ENGINE': 'django.db.backends.sqlite3',        'NAME': ':memory:'    }}# 禁用密码哈希加速PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]# 关闭调试模式DEBUG = FalseTEMPLATE_DEBUG = False

核心依赖安装

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├── models.py└── views.py

五大核心测试类型

模型测试 (test_models.py)

from django.test import TestCasefrom .models import Productfrom .factories import ProductFactoryclass 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}/')

视图测试 (test_views.py)

from django.urls import reversefrom rest_framework.test import APITestCaseclass 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)

表单测试 (test_forms.py)

from django.test import TestCasefrom .forms import ProductFormclass 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)

API测试 (DRF)

from rest_framework import statusfrom .factories import UserFactoryclass 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")

中间件/工具测试

from django.test import RequestFactoryfrom .middleware import CustomMiddlewareclass 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)

高效测试数据管理

使用Factories (factory_boy)

# factories.pyimport factoryfrom .models import Product, Userclass 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 = Trueclass 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)

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 外部依赖

模拟第三方 API

from unittest.mock import patchfrom .services import PaymentServiceclass PaymentTest(TestCase):    @patch('myapp.services.requests.post')    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)

模拟时间

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

测试覆盖率优化

配置 coverage.py

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

执行测试并生成报告

coverage run -m pytestcoverage html

测试运行优化

并行测试

pytest -n auto

只运行失败用例

pytest --lfpytest --ff

标记慢测试

@pytest.mark.slowdef test_slow_operation():    time.sleep(5)pytest -m "not slow"

测试最佳实践

测试金字塔策略

测试金字塔策略强调测试的价值与开发工作的平衡。通过优先测试高风险或高复杂度的功能,可以最大限度地减少遗留问题。

核心原则

原则 说明
FAST原则 测试应快速、孤立、可重复、自验证、及时的
Red-Green-Refactor 测试驱动开发流程
最小化数据库交互 使用mock和内存数据库来加速测试
单断言原则 每个测试应验证一个行为
最小化测试数据 使用工厂创建测试数据
注重接口行为 关注外部用户看到的接口行为,而非内部实现

常见缺陷规避

  • 避免测试间依赖关系
  • 避免使用业务逻辑测试数据
  • 避免过度测试实现细节
  • 使用工厂创建测试数据
  • 关注接口行为而非内部实现

Django 测试框架对比

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

推荐组合

pytest + pytest-django + factory_boy + coverage

完整示例项目:Django Testing Example

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

转载地址:http://pprfk.baihongyu.com/

你可能感兴趣的文章