pytest-django-patterns

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

pytest-django Testing Patterns

pytest-django 测试模式

TDD Workflow (RED-GREEN-REFACTOR)

TDD工作流(红-绿-重构)

Always follow this cycle:
  1. RED: Write a failing test first that describes desired behavior
  2. GREEN: Write minimal code to make the test pass
  3. REFACTOR: Clean up code while keeping tests green
  4. REPEAT: Never write production code without a failing test
Critical rule: If implementing a feature or fixing a bug, write the test BEFORE touching production code.
请始终遵循此循环:
  1. :先编写一个失败的测试,描述期望的行为
  2. 绿:编写最少的代码让测试通过
  3. 重构:在保持测试通过的同时清理代码
  4. 重复:永远不要在没有失败测试的情况下编写生产代码
重要规则:如果要实现功能或修复bug,在修改生产代码之前先编写测试。

Essential pytest-django Patterns

核心pytest-django模式

Database Access

数据库访问

  • Use
    @pytest.mark.django_db
    on any test touching the database
  • Apply to entire module:
    pytestmark = pytest.mark.django_db
  • Transactions roll back automatically after each test
  • 在任何涉及数据库的测试上使用
    @pytest.mark.django_db
    装饰器
  • 应用到整个模块:
    pytestmark = pytest.mark.django_db
  • 每个测试完成后事务会自动回滚

Fixtures for Test Data

测试数据的Fixtures

Use Factory Boy for models, pytest fixtures for setup:
  • Factories: Create model instances with realistic data (
    UserFactory()
    )
    • Use
      factory.Sequence()
      for unique fields
    • Use
      factory.Faker()
      for realistic fake data
    • Use
      factory.SubFactory()
      for foreign keys
    • Use
      @factory.post_generation
      for M2M relationships
  • Fixtures: Setup clients, auth state, or shared resources
    • client
      fixture: Django test client
    • Create
      auth_client
      fixture:
      client.force_login(user)
      for authenticated requests
    • Define in
      conftest.py
      for reuse across test files
使用Factory Boy处理模型,pytest fixtures处理前置设置:
  • 工厂:创建带有真实数据的模型实例(
    UserFactory()
    • 使用
      factory.Sequence()
      生成唯一字段
    • 使用
      factory.Faker()
      生成真实的模拟数据
    • 使用
      factory.SubFactory()
      处理外键关联
    • 使用
      @factory.post_generation
      处理多对多关系
  • Fixtures:设置客户端、认证状态或共享资源
    • client
      fixture:Django测试客户端
    • 创建
      auth_client
      fixture:通过
      client.force_login(user)
      实现已认证请求
    • conftest.py
      中定义,以便在多个测试文件中复用

Test Organization

测试组织

Structure tests to mirror app structure:
tests/
├── apps/
│   └── posts/
│       ├── test_models.py
│       ├── test_views.py
│       └── test_forms.py
├── factories.py
└── conftest.py
Group related tests in classes:
  • Name classes
    TestComponentName
    (e.g.,
    TestPostListView
    )
  • Name test methods descriptively:
    test_<action>_<expected_outcome>
  • Use
    @pytest.mark.parametrize
    for testing multiple scenarios
测试结构与应用结构保持一致:
tests/
├── apps/
│   └── posts/
│       ├── test_models.py
│       ├── test_views.py
│       └── test_forms.py
├── factories.py
└── conftest.py
将相关测试分组到类中:
  • 类命名为
    TestComponentName
    (例如
    TestPostListView
  • 测试方法命名要具有描述性:
    test_<动作>_<预期结果>
  • 使用
    @pytest.mark.parametrize
    测试多种场景

What to Test

测试内容

Views

视图

  • Status codes: Correct HTTP responses (200, 404, 302)
  • Authentication: Authenticated vs anonymous behavior
  • Authorization: User can only access their own data
  • Context data: Correct objects passed to template
  • Side effects: Database changes, emails sent, tasks queued
  • HTMX: Check
    HTTP_HX_REQUEST
    header returns partial template
  • 状态码:正确的HTTP响应(200、404、302)
  • 认证:已认证用户与匿名用户的行为差异
  • 授权:用户只能访问自己的数据
  • 上下文数据:传递给模板的对象正确
  • 副作用:数据库变更、邮件发送、任务入队
  • HTMX:检查
    HTTP_HX_REQUEST
    头是否返回部分模板

Forms

表单

  • Validation: Valid data passes, invalid data fails with correct errors
  • Edge cases: Empty fields, max lengths, unique constraints
  • Clean methods: Custom validation logic works
  • Save behavior: Objects created/updated correctly
  • 验证:有效数据通过验证,无效数据返回正确错误信息
  • 边界情况:空字段、最大长度、唯一约束
  • Clean方法:自定义验证逻辑正常工作
  • 保存行为:对象被正确创建/更新

Models

模型

  • Methods:
    __str__
    , custom methods return expected values
  • Managers/QuerySets: Custom filtering works correctly
  • Constraints: Database-level validation enforced
  • Signals: Pre/post save hooks execute correctly
  • 方法
    __str__
    、自定义方法返回预期值
  • 管理器/查询集:自定义过滤逻辑正常工作
  • 约束:数据库级别的验证被正确执行
  • 信号:保存前后的钩子函数正确执行

Celery Tasks

Celery任务

  • Mock external calls: Patch HTTP requests, email sending, etc.
  • Test logic only: Don't test actual async execution
  • Idempotency: Running task multiple times is safe
  • 模拟外部调用:Patch HTTP请求、邮件发送等操作
  • 仅测试逻辑:不要测试实际的异步执行过程
  • 幂等性:多次执行任务是安全的

Django-Specific Testing Patterns

Django特定测试模式

Testing HTMX Responses

测试HTMX响应

Check partial template rendered when
HX-Request
header present:
  • Pass
    HTTP_HX_REQUEST="true"
    to client request
  • Assert
    response.templates
    contains partial template name
当存在
HX-Request
头时,检查是否渲染了部分模板:
  • 向客户端请求传递
    HTTP_HX_REQUEST="true"
  • 断言
    response.templates
    包含部分模板名称

Testing Permissions

测试权限

Create authenticated vs anonymous client fixtures:
  • Test redirect/403 for unauthorized access
  • Test success for authorized access
创建已认证和匿名客户端fixture:
  • 测试未授权访问时的重定向/403状态
  • 测试授权访问时的成功响应

Testing QuerySets

测试查询集

Verify efficient queries:
  • Create test data with factories
  • Execute query
  • Assert correct objects returned/excluded
  • Verify related objects loaded with
    select_related()
    /
    prefetch_related()
验证查询的效率:
  • 使用工厂创建测试数据
  • 执行查询
  • 断言返回/排除的对象正确
  • 验证通过
    select_related()
    /
    prefetch_related()
    加载关联对象

Testing Forms with Model Instances

测试带模型实例的表单

Pass instance to form for updates:
  • form = MyForm(data=new_data, instance=existing_obj)
  • Verify
    form.save()
    updates, doesn't create
传递实例给表单以进行更新:
  • form = MyForm(data=new_data, instance=existing_obj)
  • 验证
    form.save()
    执行更新而非创建

Common Patterns

通用模式

Parametrize multiple scenarios: Use
@pytest.mark.parametrize("input,expected", [...])
for testing various inputs
Mock external services: Use
mocker.patch()
to avoid actual HTTP calls, emails, file operations
Check database changes:
  • Assert
    Model.objects.filter(...).exists()
    after creation
  • Assert
    Model.objects.count() == expected
    for deletions
  • Use
    refresh_from_db()
    to verify updates
Test error handling:
  • Invalid form data produces correct errors
  • Failed operations return error responses
  • User sees appropriate error messages
参数化多种场景: 使用
@pytest.mark.parametrize("input,expected", [...])
测试各种输入
模拟外部服务: 使用
mocker.patch()
避免实际的HTTP调用、邮件发送、文件操作
检查数据库变更:
  • 创建操作后断言
    Model.objects.filter(...).exists()
  • 删除操作后断言
    Model.objects.count() == expected
  • 使用
    refresh_from_db()
    验证更新
测试错误处理:
  • 无效表单数据返回正确错误信息
  • 失败操作返回错误响应
  • 用户看到合适的错误提示

Running Tests

运行测试

bash
uv run pytest                    # All tests
uv run pytest -x                 # Stop on first failure
uv run pytest --lf               # Run last failed
uv run pytest -x --lf            # Stop first, last failed only
uv run pytest -k "test_name"     # Run tests matching pattern
uv run pytest tests/apps/posts/  # Specific directory
uv run pytest --cov=apps         # With coverage report
bash
uv run pytest                    # 运行所有测试
uv run pytest -x                 # 遇到第一个失败即停止
uv run pytest --lf               # 运行最后失败的测试
uv run pytest -x --lf            # 仅运行最后失败的测试,遇到第一个失败即停止
uv run pytest -k "test_name"     # 运行匹配指定模式的测试
uv run pytest tests/apps/posts/  # 运行指定目录下的测试
uv run pytest --cov=apps         # 生成覆盖率报告

Common Pitfalls

常见陷阱

  • Forgetting
    @pytest.mark.django_db
    : Results in "Database access not allowed" errors
  • Not using factories: Creating instances manually is verbose and brittle
  • Testing implementation: Test behavior and outcomes, not internal implementation details
  • Skipping TDD: Writing tests after code means tests follow implementation, missing edge cases
  • Over-mocking: Mock external dependencies, not your own code
  • Testing framework code: Don't test Django's ORM, form validation, etc. Test YOUR logic
  • 忘记使用
    @pytest.mark.django_db
    :会导致“不允许数据库访问”错误
  • 不使用工厂:手动创建实例繁琐且脆弱
  • 测试实现细节:测试行为和结果,而非内部实现细节
  • 跳过TDD:在编写代码后再写测试会导致测试跟随实现,遗漏边界情况
  • 过度模拟:只模拟外部依赖,不要模拟自己的代码
  • 测试框架代码:不要测试Django的ORM、表单验证等,只测试你自己的逻辑

Setup Requirements

配置要求

In
pyproject.toml
:
toml
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "config.settings.test"
python_files = ["test_*.py"]
addopts = ["--reuse-db", "-ra"]
In
conftest.py
:
Define shared fixtures (auth_client, common factories, etc.)
pyproject.toml
中:
toml
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "config.settings.test"
python_files = ["test_*.py"]
addopts = ["--reuse-db", "-ra"]
conftest.py
中:
定义共享fixture(auth_client、通用工厂等)

Integration with Other Skills

与其他技能的集成

  • systematic-debugging: When fixing bugs, write failing test first to reproduce
  • django-models: Test custom managers, QuerySets, and model methods
  • django-forms: Test form validation, clean methods, and save behavior
  • celery-patterns: Test task logic with mocked external dependencies
  • systematic-debugging:修复bug时,先编写失败测试以复现问题
  • django-models:测试自定义管理器、查询集和模型方法
  • django-forms:测试表单验证、clean方法和保存行为
  • celery-patterns:通过模拟外部依赖测试任务逻辑