pytest-django测试后Docker中PostgreSQL数据库未清理问题排查
问题分析与解决方案
核心原因
你遇到的问题大概率是自定义的django_db_setup fixture覆盖了pytest-django默认的事务隔离机制,导致测试结束后没有自动回滚数据。默认情况下,pytest-django会为每个django_db标记的测试开启独立事务,测试完成后回滚,避免数据残留。但你自定义了django_db_setup,跳过了这部分逻辑,测试数据直接写入数据库并持久化,重复测试时就触发唯一键冲突。
具体解决步骤
1. 移除自定义的django_db_setup fixture(推荐)
pytest-django支持通过配置文件或环境变量设置数据库连接,不需要手动重写django_db_setup:
- 在项目根目录创建
pytest.ini,添加数据库配置:
[pytest] DJANGO_SETTINGS_MODULE = your_project.settings django_db_host = postgres django_db_name = name
- 确保你的Django settings中数据库配置支持通过环境变量或pytest配置覆盖,比如:
# settings.py import os DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'HOST': os.getenv('DB_HOST', 'localhost'), 'NAME': os.getenv('DB_NAME', 'default_db'), 'USER': os.getenv('DB_USER', 'postgres'), 'PASSWORD': os.getenv('DB_PASSWORD', 'postgres'), } }
这样pytest-django会自动处理数据库初始化和事务回滚,测试数据不会残留。
2. 改用transactional_db标记替代django_db
如果必须保留自定义的django_db_setup,可以把测试标记换成@pytest.mark.transactional_db,强制pytest-django为每个测试开启事务并在结束后回滚:
@pytest.mark.transactional_db # 替换原来的django_db @pytest.mark.parametrize("parm1,parm2,expected1,expected2", [ ('000-01', 'NEW', 'NEW', '000-01'), ('000-02', 'NEW2', 'NEW2', '000-02'), ]) def test_add_model(parm1,parm2,expected1,expected2): obj = AddModel(field1=parm1, field2=parm2) new = obj.create() assert new.field1 == expected1 assert new.field2 == expected2
3. 手动在fixture中添加事务支持
如果需要完全自定义数据库配置,同时保留事务隔离,可以修改django_db_setup fixture,确保启用事务:
import pytest from django.db import transaction from django.conf import settings @pytest.fixture(scope="function") def django_db_setup(): settings.DATABASES['default'] = { 'ENGINE': 'django.db.backends.postgresql', 'HOST': 'postgres', 'NAME': 'name', 'USER': 'postgres', 'PASSWORD': 'postgres', } # 确保每个测试都在事务中执行,结束后强制回滚 with transaction.atomic(): yield transaction.set_rollback(True)
4. Docker层面排查(次要)
如果以上方法无效,检查PostgreSQL容器是否挂载了本地持久化卷:
- 查看
docker-compose.yml,如果数据库服务有volumes配置挂载本地目录,测试后数据会持久化到本地,下次启动容器时数据还在。这种情况可以去掉卷挂载,或者在测试前手动清空数据库。
内容的提问来源于stack exchange,提问作者bobi
相关产品推荐
相关产品推荐

