为Django项目的Pytest测试套件搭建PostgreSQL测试数据库遇问题
问题分析与解决方案
现有代码的核心问题
你当前的代码只创建了测试数据库,但没有告诉Django切换到这个新数据库,Django依然使用settings.py中配置的原数据库,这就是测试始终连接原库的核心原因。此外还有两个关键遗漏:
- 测试数据库创建后未执行Django迁移,数据库是空的,无法支持模型操作
- 你的数据库路由逻辑(
app_name == database_name)可能导致Django找不到对应app的测试库(你只创建了test_db,但路由可能期望每个app对应独立数据库)
修复现有Fixture的方案
修改你的fixture,添加Django配置切换、迁移执行,并处理路由问题:
import pytest import psycopg2 import os from django.conf import settings from django.core.management import call_command from django.db import connections @pytest.fixture(scope='session') def postgresql_db(): db_name = 'test_db' # 连接到管理员数据库(如postgres默认库)创建测试库 admin_conn = psycopg2.connect( dbname=os.environ.get('POSTGRES_DB', 'postgres'), user=os.environ.get('POSTGRES_USER', 'postgres'), password=os.environ.get('POSTGRES_PASSWORD', ''), host=os.environ.get('POSTGRES_HOST', 'localhost'), port=os.environ.get('POSTGRES_PORT', '5432'), ) admin_conn.autocommit = True # 创建数据库必须开启自动提交 # 先删除旧测试库(如果存在)再创建新库 with admin_conn.cursor() as cursor: cursor.execute(f"DROP DATABASE IF EXISTS {db_name};") cursor.execute(f"CREATE DATABASE {db_name};") # 保存原数据库配置,切换到测试库 original_db_config = settings.DATABASES['default'].copy() settings.DATABASES['default']['NAME'] = db_name # 关闭所有现有连接,强制Django重新连接新库 for conn in connections.all(): conn.close() # 执行迁移,生成测试库表结构 call_command('migrate', '--noinput') yield db_name # 清理:恢复原配置,关闭连接,删除测试库 settings.DATABASES['default'] = original_db_config for conn in connections.all(): conn.close() with admin_conn.cursor() as cursor: cursor.execute(f"DROP DATABASE IF EXISTS {db_name};") admin_conn.close() # 针对数据库路由的临时覆盖Fixture @pytest.fixture(scope='session') def override_db_router(): # 保存原路由配置 original_routers = settings.DATABASE_ROUTERS # 定义临时路由,让所有app操作都走default测试库 class TestRouter: def db_for_read(self, model, **hints): return 'default' def db_for_write(self, model, **hints): return 'default' def allow_relation(self, obj1, obj2, **hints): return True def allow_migrate(self, db, app_label, model_name=None, **hints): return True # 替换路由 settings.DATABASE_ROUTERS = ['__main__.TestRouter'] yield # 恢复原路由 settings.DATABASE_ROUTERS = original_routers
使用时,在测试函数中同时调用两个fixture:
def test_db_operation(postgresql_db, override_db_router): # 你的测试逻辑
更标准的测试数据库管理方案
手动写fixture容易出错,推荐使用pytest-django插件,它已经封装了所有测试数据库的操作:
步骤1:安装插件
pip install pytest-django
步骤2:运行测试
直接执行pytest,插件会自动:
- 创建以原数据库名加
_test后缀的测试库 - 自动执行Django迁移
- 测试完成后自动销毁测试库
自定义配置(可选)
在项目根目录创建pytest.ini,自定义测试库名或其他参数:
[pytest] DJANGO_SETTINGS_MODULE = your_project.settings addopts = --db-name=test_custom_db # 指定测试库名
处理数据库路由
如果需要临时覆盖路由,同样可以用前面的override_db_router fixture,或在测试中直接修改settings.DATABASE_ROUTERS。
额外说明
权限错误可能来自两个原因:
- 创建测试库的用户(如
postgres)没有足够权限,确保该用户有CREATEDB权限 - Django配置的数据库用户没有访问测试库的权限,创建测试库后可以执行
GRANT ALL PRIVILEGES ON DATABASE test_db TO your_django_user;授权
内容的提问来源于stack exchange,提问作者sclem72
相关产品推荐
相关产品推荐

