指定--reuse-db后测试数据库仍被清空,如何实现预期复用效果?
问题分析与解决方案
首先要明确两个核心概念的区别:
--reuse-db是不同测试运行之间复用数据库结构(避免每次都重建表、跑迁移),不影响同一测试会话内多个测试函数的数据隔离。- 你遇到的是同一测试会话中,
test1能看到初始用户、test2看不到的问题,本质是测试函数的事务隔离机制导致的数据不共享。
问题根源
- 数据库路径修改时机错误:你在
django_db_setupfixture中动态修改settings.DATABASES,但pytest-django可能已经在会话初始化阶段建立了数据库连接,导致部分测试函数使用的还是默认测试数据库,而非你指定的test-db.sqlite3。 - 事务隔离默认行为:pytest-django默认给每个测试函数包裹独立事务,测试结束后自动回滚。虽然你的初始用户是在会话级fixture中持久化创建的,但SQLite的事务隔离特性或pytest-django的内部逻辑,可能导致后续测试函数无法读取到事务外的持久化数据。
具体解决方案
步骤1:提前设置测试数据库路径
在conftest.py中使用pytest_sessionstart钩子,在会话启动初期就修改数据库配置,避免连接初始化后再修改失效:
def pytest_sessionstart(session): from django.conf import settings db_path = settings.BASE_DIR / 'test-db.sqlite3' settings.DATABASES['default']['NAME'] = db_path
步骤2:调整会话级初始化逻辑
修改django_db_setup fixture,去掉重复修改配置的代码,同时避免重复创建用户:
import pytest import uuid from django.contrib.auth.models import User from django.core.management import call_command @pytest.fixture(scope='session') def test_user(): return { 'username': uuid.uuid4().hex[:10], 'password': uuid.uuid4().hex, } @pytest.fixture(scope='session') def django_db_setup(django_db_blocker, test_user): with django_db_blocker.unblock(): # 仅执行必要的迁移(--reuse-db会保留已有表结构) call_command('migrate', '--noinput') # 用get_or_create避免重复创建用户 User.objects.get_or_create( username=test_user['username'], defaults={ 'password': test_user['password'], 'email': f'{test_user["username"]}@test', 'is_active': True, } )
步骤3:关闭测试函数的事务隔离
给测试函数添加transaction=False标记,让测试直接操作数据库而非在事务中运行,确保能读取到会话级fixture创建的持久化数据:
import pytest @pytest.mark.django_db(transaction=False) def test1(django_user_model): print(django_user_model.objects.all()) @pytest.mark.django_db(transaction=False) def test2(django_user_model): print(django_user_model.objects.all())
验证效果
运行测试后,test1和test2都会输出包含初始用户的QuerySet,符合你的预期结果。同时--reuse-db会保留test-db.sqlite3数据库,下次运行测试时无需重新创建表和跑迁移,提升启动速度。
内容的提问来源于stack exchange,提问作者nlblack323
相关产品推荐
相关产品推荐

