代码之家  ›  专栏  ›  技术社区  ›  faceclean

如何组织数据库访问层?

  •  14
  • faceclean  · 技术社区  · 16 年前

    我正在使用SqlAlchemy,一个python ORM库。我以前通过调用SqlAlchemy API直接从业务层访问数据库。

    但后来我发现这会导致运行所有测试用例的时间过长,现在我想也许我应该创建一个数据库访问层,这样我就可以在测试期间使用模拟对象,而不是直接访问数据库。

    您将如何组织数据库层?

    4 回复  |  直到 16 年前
        1
  •  6
  •   rob    16 年前

    这个问题问得好!
    这个问题并非微不足道,可能需要多种方法来解决。

    1. 组织代码,以便您可以在不访问数据库的情况下测试大多数应用程序逻辑。这意味着每个类都有访问数据的方法和处理数据的方法,第二个类可以很容易地进行测试。
    2. self initializing fake .
    3. 如果代码不涉及存储过程,可以考虑使用内存数据库,就像Lennart说的那样(即使在这种情况下,称之为“单元测试”听起来可能有点奇怪!)。

    最后,我认为这主要是一个问题 .

        2
  •  2
  •   Lennart Regebro    16 年前

    我会在测试期间设置一个数据库连接,连接到内存中的数据库。像这样:

    sqlite_memory_db = create_engine('sqlite://')
    

        3
  •  2
  •   Ants Aasma    16 年前

    捕获数据库修改的一种方法是使用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'
    

    但无论如何,您都希望至少对数据库进行一些测试,因为您至少想知道您的查询是否按预期工作。请记住,您还可以通过以下方式修改数据库 session.update() , .delete() .execute() .

        4
  •  0
  •   brool    16 年前

    SQLAlchemy有一些功能 making mocking easier