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

为Django项目的Pytest测试套件搭建PostgreSQL测试数据库遇问题

问题分析与解决方案

现有代码的核心问题

你当前的代码只创建了测试数据库,但没有告诉Django切换到这个新数据库,Django依然使用settings.py中配置的原数据库,这就是测试始终连接原库的核心原因。此外还有两个关键遗漏:

  1. 测试数据库创建后未执行Django迁移,数据库是空的,无法支持模型操作
  2. 你的数据库路由逻辑(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。

额外说明

权限错误可能来自两个原因:

  1. 创建测试库的用户(如postgres)没有足够权限,确保该用户有CREATEDB权限
  2. Django配置的数据库用户没有访问测试库的权限,创建测试库后可以执行GRANT ALL PRIVILEGES ON DATABASE test_db TO your_django_user;授权

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 07:20:07