如何在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
相关产品推荐
相关产品推荐

