You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django中正确使用pytest fixtures创建可访问的测试对象?

问题

刚接触pytest,已经掌握简单fixture用法:

@pytest.fixture
def example_data():
    return "abc"

测试中这样使用:

def test_data(self, example_data):
    assert example_data == "abc"

但在Django项目中用fixture创建测试对象时遇到问题:尝试写了如下fixture创建用户:

@pytest.fixture
def test_data(self):  
    users = get_user_model()
    client = users.objects.get_or_create(username="test_user", password="password")

然后在测试中想访问该用户:

@pytest.mark.django_db
@pytest.mark.usefixtures("test_data")
async def test_get_users(self):
    # 期望该用户在查询集中
    all_users = await sync_to_async(User.objects.all)()
    # ... 执行断言 ...

结果查询不到fixture里创建的用户。另外,在测试函数内直接创建对象是可行的,但没法满足参数化、给不同用户分配不同分组的需求;试过用测试类的__init__或setup创建用户,既不是pytest推荐方式,也同样查不到。想知道怎么创建能被查询集访问的测试对象,有没有pytest的标准实现方式,而非每个用例手动创建。


解决方案

1. 修复Fixture的核心问题

你的test_data fixture存在几个关键问题,修正后就能正常工作:

  • Fixture不需要self参数(类级fixture除外,这里没必要)
  • get_or_create返回元组(对象, 创建状态),需正确接收返回值
  • 直接设置password不会加密,要用set_password方法处理
  • 确保fixture操作在数据库事务中生效

修正后的fixture:

import pytest
from django.contrib.auth import get_user_model

User = get_user_model()

@pytest.fixture
def test_user():
    # 接收get_or_create返回的用户对象,忽略创建状态
    user, _ = User.objects.get_or_create(username="test_user")
    # 加密密码并保存
    user.set_password("password")
    user.save()
    return user

2. 正确在测试中使用Fixture

有两种常用方式:

方式一:直接作为参数传入测试函数

这是pytest推荐的用法,直观且能直接拿到fixture返回的对象:

@pytest.mark.django_db
async def test_get_users(test_user):
    all_users = await sync_to_async(User.objects.all)()
    assert test_user in all_users

@pytest.mark.django_db标记会自动赋予测试数据库访问权限,无需额外配置。

方式二:用usefixtures标记(适合多fixture场景)

如果不想把fixture作为参数传入,可使用标记,之后通过ORM查询获取对象:

@pytest.mark.django_db
@pytest.mark.usefixtures("test_user")
async def test_get_users(self):
    all_users = await sync_to_async(User.objects.all)()
    test_user = await sync_to_async(User.objects.get)(username="test_user")
    assert test_user in all_users

3. 参数化Fixture实现多用户/分组场景

要实现参数化生成不同用户(比如分配不同分组),可以给fixture添加params参数:

import pytest
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group

User = get_user_model()

@pytest.fixture(params=[
    ("user_admin", "admin_group"),
    ("user_editor", "editor_group"),
])
def parametrized_user(request):
    username, group_name = request.param
    # 创建分组
    group, _ = Group.objects.get_or_create(name=group_name)
    # 创建用户并关联分组
    user, _ = User.objects.get_or_create(username=username)
    user.set_password("password")
    user.groups.add(group)
    user.save()
    return user, group

测试中使用参数化fixture,pytest会自动遍历所有参数生成测试用例:

@pytest.mark.django_db
async def test_users_with_groups(parametrized_user):
    user, group = parametrized_user
    # 验证用户属于对应分组
    assert group in await sync_to_async(lambda: user.groups.all())()
    # 验证数据库中能查到关联关系
    db_user = await sync_to_async(User.objects.get)(username=user.username)
    assert await sync_to_async(lambda: db_user.groups.filter(name=group.name).exists())()

4. 异步测试注意事项

所有Django ORM操作必须用sync_to_async包裹,避免同步/异步代码混用导致的数据库连接异常。


内容的提问来源于stack exchange,提问作者Flora Biletsiou

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 02:06:26