SQLAlchemy 2.0测试Mock失效致数据入库,求解决方案
解决SQLAlchemy仓库测试时写入真实数据库的问题
测试SQLAlchemy 2.0仓库时碰到唯一约束冲突错误:
sqlalchemy.exc.IntegrityError: (psycopg2.errors.UniqueViolation) duplicate key value violates unique constraint "professions_name_key"
明明用了pytest-mock做Mock测试,但数据还是被写入真实数据库,怎么避免?
问题根源
你当前的Mock策略不对:
- 仓库代码里是通过
session = Session()创建Session实例来操作数据库,但你只Mock了Session类的add/commit/refresh方法,这不会影响真实的Session实例创建和执行。 - 你Mock的
ProfessionsDBModel是类本身,但仓库里是创建这个类的实例,断言时传类对象也不符合实际逻辑。
解决方案
要彻底隔离真实数据库,核心是让仓库拿到的Session是一个Mock实例,所有数据库操作都走这个Mock,不碰真实库。具体调整如下:
修正后的测试代码
import uuid import pytest from src.domain.entities.profession import Profession from src.infra.repositories.profession_postgresql_repository import ProfessionPostgresqlRepository def test_profession_postgresql_repository(mocker, fixture_profession_developer): # Mock uuid生成逻辑 mocker.patch( 'uuid.uuid4', return_value=fixture_profession_developer["profession_id"] ) # 1. Mock仓库中使用的Session类,让它返回一个Mock实例 mock_session = mocker.Mock() mocker.patch( 'src.infra.repositories.profession_postgresql_repository.Session', return_value=mock_session ) # 2. Mock ProfessionsDBModel类,捕获实例化后的对象 mock_profession_db_cls = mocker.patch( 'src.infra.repositories.profession_postgresql_repository.ProfessionsDBModel' ) # 执行仓库方法 repository = ProfessionPostgresqlRepository() result = repository.create( fixture_profession_developer["name"], fixture_profession_developer["description"] ) # 3. 断言Session实例的方法被正确调用 mock_session.add.assert_called_once_with(mock_profession_db_cls.return_value) mock_session.commit.assert_called_once() mock_session.refresh.assert_called_once_with(mock_profession_db_cls.return_value) # 可选:断言转换后的实体是否符合预期 assert result.profession_id == fixture_profession_developer["profession_id"] assert result.name == fixture_profession_developer["name"]
关键改动说明
- Mock正确的Session路径:Python的Mock基于导入路径生效,仓库里是从
db_base导入Session到自身模块,所以必须在仓库模块的路径下MockSession,才能覆盖真实的导入。 - 用Mock实例替代真实Session:让
Session()返回一个Mock对象,这样仓库里的session.add/commit/refresh都是调用这个Mock实例的方法,不会触发真实数据库操作。 - 修正断言逻辑:用
assert_called_once_with做方法调用断言,并且传入mock_profession_db_cls.return_value(即类实例化后的对象),符合仓库代码的实际执行逻辑。
内容的提问来源于stack exchange,提问作者Claudio Shigueo Watanabe
相关产品推荐
相关产品推荐

