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

如何让pytest-django将数据提交到数据库而非仅存在事务中

核心原因
  • @pytest.mark.django_db标记仅对测试函数/测试类生效,你将该标记写在fixture定义上不会被pytest识别,你的测试仍然运行在默认的transaction=False模式下:pytest会为每个测试包裹一层自动回滚的事务,所有写入操作仅在该事务内可见,测试执行结束后直接回滚,不会真实提交到测试数据库的物理存储。
  • 你在pdb中能查到数据,是因为调试时fixture代码还在当前未回滚的事务上下文中,而GraphQL/REST请求会触发Django开启独立的请求数据库连接,无法读取未提交的事务数据,所以返回空结果。
  • 你给fixture函数设置属性缓存fake_user.user仅缓存了Python层的User对象实例,和数据库存储无关,事务回滚后该对象属于数据库游离态,不能代表数据库真实存储。
解决方案

方案1:开启事务提交模式(数据真实写入测试库)

将@pytest.mark.django_db(transaction=True)标记加在需要用到该fixture的测试函数/测试类上,不要加在fixture定义上:

# 测试函数写法
@pytest.mark.django_db(transaction=True)
def test_my_api(fake_user, client):
    response = client.get("/api/users/")
    assert response.status_code == 200
    # 此时请求能查到fake_user的数据

该模式下pytest会禁用自动回滚的包裹事务,所有数据库操作会真实提交,测试结束后pytest会执行flush清空表数据,不会保留到下一轮测试。

方案2:保留测试数据库便于调试

运行pytest时添加--reuse-db参数,pytest不会在测试结束后删除测试数据库,你可以直接连接到测试库(默认库名格式为test_<你的Django配置中DB_NAME>)查看写入的真实数据。

优化fixture写法

你不需要用函数属性缓存user实例,直接调整fixture作用域即可实现单例效果,修改后的fixture如下:

import pytest
from django.utils import timezone
from django.contrib.auth import get_user_model

User = get_user_model()

@pytest.fixture(scope="module") # 整个测试模块只创建一次
def fake_user(django_db_setup, django_db_blocker):
    with django_db_blocker.unblock():
        user, _ = User.objects.get_or_create(
            id=1,
            defaults=dict(
                is_superuser=False,
                is_staff=False,
                email='foo@barmail.com', 
                username='foobaruser',
                password='passwordmaster',
                date_joined=timezone.now()
            )
        )
    return user

内容的提问来源于stack exchange,提问作者Artem Bulatov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 05:27:04