Pytest fixture初始化测试数据库种子数据报错排查
问题原因
你遇到的AttributeError: 'function' object has no attribute 'add'和fixture注入机制没有关系,是最基础的Python作用域规则导致的:
- pytest的fixture自动注入仅对两类函数生效:一是pytest收集到的测试用例函数,二是被
@pytest.fixture装饰的fixture函数,注入逻辑是匹配函数参数列表里的参数名,找到对应fixture传进去。 - 你写的
seed_database是普通自定义函数,不在pytest的注入范围内,调用它的时候也没有手动传入任何参数。它内部引用的db_session,会直接匹配当前模块全局作用域下的db_session对象——也就是你用@pytest.fixture装饰的那个fixture函数本身,类型是function,自然没有add方法。
另外你原本的设计还有逻辑矛盾:setup_database是session级别的fixture(整个测试运行周期只执行一次),而db_session是默认function级别的fixture(每个测试用例执行一次),pytest不允许宽作用域的fixture依赖窄作用域的fixture,就算强行把db_session注入到setup_database里也会报作用域不匹配错误。
修改方案
按照以下步骤调整代码即可:
- 改造
seed_database函数,让它显式接收session参数,不再依赖全局变量:
def seed_database(session): users = [ { "id": 1, "name": "John Doe", }, # 其余种子数据 ] for user in users: db_user = User(**user) session.add(db_user) session.commit()
- 修改
setup_databasefixture,在播种阶段单独创建一个临时会话执行种子数据插入,插入完成后清理会话:
@pytest.fixture(scope="session") def setup_database(connection): models.Base.metadata.bind = connection models.Base.metadata.create_all() # 初始化播种专用会话 seed_session = scoped_session( sessionmaker(autocommit=False, autoflush=False, bind=connection) ) seed_database(seed_session) seed_session.remove() # 播种完成后清理会话,避免残留 yield models.Base.metadata.drop_all()
- 优化
db_sessionfixture,在用例执行完后补充scoped_session的清理逻辑,避免会话跨用例残留:
@pytest.fixture def db_session(setup_database, connection): transaction = connection.begin() session = scoped_session( sessionmaker(autocommit=False, autoflush=False, bind=connection) ) yield session transaction.rollback() session.remove()
调整后流程逻辑通顺:整个测试启动时先建表、插入一次种子数据,每个测试用例启动时开新事务,用例跑完回滚事务不会污染种子数据,所有测试执行完后自动删表。
内容的提问来源于stack exchange,提问作者zilla
相关产品推荐
相关产品推荐

