Python 如何组织数据库访问层?
我正在使用SqlAlchemy,一个python ORM库。我曾经通过调用SqlAlchemy API直接从业务层访问数据库 但后来我发现这会导致运行所有测试用例的时间过长,现在我想我应该创建一个DB访问层,这样我可以在测试期间使用模拟对象,而不是直接访问数据库 我认为有两种选择:Python 如何组织数据库访问层?,python,database,testing,orm,mocking,Python,Database,Testing,Orm,Mocking,我正在使用SqlAlchemy,一个python ORM库。我曾经通过调用SqlAlchemy API直接从业务层访问数据库 但后来我发现这会导致运行所有测试用例的时间过长,现在我想我应该创建一个DB访问层,这样我可以在测试期间使用模拟对象,而不是直接访问数据库 我认为有两种选择: 使用一个包含DB连接的类和许多方法,如addUser/delUser/updateUser、addBook/delBook/updateBook。但这意味着这个班级将非常大 另一种方法是创建不同的管理器类,如“Use
如何组织数据库层?我会在测试期间设置一个数据库连接,改为连接内存中的数据库。像这样:
sqlite_memory_db = create_engine('sqlite://')
这将是你能得到的最快的速度,你也没有连接到一个真正的数据库,只是内存中的一个临时数据库,所以你不必担心测试后剩下的测试所做的更改,等等,你不必嘲笑任何东西。SQLAlchemy有一些功能——也许这比试图重写项目的整个部分更容易 这是个好问题
这个问题并非微不足道,可能需要几种方法来解决。 例如:
不幸的是,这并不总是正确的(许多人认为单元测试可以在一夜之间运行,所以时间不是问题),并且您得到的东西将不是真正的单元测试。一种捕获对数据库的修改的方法,是使用SQLAlchemy会话扩展机制,并使用如下内容拦截对数据库的刷新:
from sqlalchemy.orm.attributes import instance_state
from sqlalchemy.orm import SessionExtension
class MockExtension(SessionExtension):
def __init__(self):
self.clear()
def clear(self):
self.updates = set()
self.inserts = set()
self.deletes = set()
def before_flush(self, session, flush_context, instances):
for obj in session.dirty:
self.updates.add(obj)
state = instance_state(obj)
state.commit_all({})
session.identity_map._mutable_attrs.discard(state)
session.identity_map._modified.discard(state)
for obj in session.deleted:
self.deletes.add(obj)
session.expunge(obj)
self.inserts.update(session.new)
session._new = {}
然后,对于测试,您可以使用该模拟配置会话,并查看它是否符合您的期望
mock = MockExtension()
Session = sessionmaker(extension=[mock], expire_on_commit=False)
def do_something(attr):
session = Session()
obj = session.query(Cls).first()
obj.attr = attr
session.commit()
def test_something():
mock.clear()
do_something('foobar')
assert len(mock.updates) == 1
updated_obj = mock.updates.pop()
assert updated_obj.attr == 'foobar'
但无论如何,您至少需要对数据库进行一些测试,因为您至少需要知道查询是否按预期工作。请记住,您还可以通过
会话.update()
,删除()
和执行()
对数据库进行修改。您好,因为我的一些同事坚持我们应该使用存储过程,而我对此没有控制权,所以sqlite对我来说不是一个可能的选择。顺便说一句:sqlite不支持storage procedure.Hm。这意味着您必须模拟代码的重要部分(存储过程)。这将使测试变得不那么有用。棘手的情况。谢谢@brool,但该链接现在已断开:(