如何在sqlalchemy中模拟创建的时间?

pro*_*sev 6 python testing unit-testing sqlalchemy mocking

我在Flask应用程序中使用SQLAlchemy。在测试中,我尝试为自己的条目模拟自动时间创建和更新。但是我不明白它如何用于SQLAlchemy。例如,我尝试使用freezgun模拟 datetime创建,但是失败。例如:

class Entry(db.Model):
    __tablename__ = 'entries'
    created = db.Column(db.DateTime(), default=db.func.now())
    updated = db.Column(db.DateTime(), default=db.func.now(), onupdate=db.func.now())

class ViewTestCase(AppliactionTestCase):
    def test(self):
        with freeze_time("2014-06-01 16:00:00"):
            db.session.add(Entry())
            db.session.commit()
        entry = db.session.query(Entry).first()
        self.assertEqual(entry.created, datetime(2014, 6, 1, 16, 0, 0))
Run Code Online (Sandbox Code Playgroud)

还有一个问题。如果需要刷新updated输入内容,如何强制保存输入内容而不进行更改?

小智 5

sqlalchemy 提供 hook sqlalchemy.eventapi 来修补查询会话。

先决条件:flask-sqlalchemy、pytest、freezengun、contextlib

以@prokoptesev 使用的flask-sqlalchemy 为例:

class Entry(db.Model):
    __tablename__ = 'entries'
    created = db.Column(db.DateTime(), default=db.func.now())
    updated = db.Column(db.DateTime(), default=db.func.now(), onupdate=db.func.now())
Run Code Online (Sandbox Code Playgroud)

只需编写一个 pytest 装置来修补行创建时间:

import datetime
from flask_sqlalchemy import event
from contextlib import contextmanager
from freezegun import freeze_time

import Entry
# if you want the code be commonly used, change Entry to db.Model instead


@contextmanager
def patch_time(time_to_freeze, tick=True):
    with freeze_time(time_to_freeze, tick=tick) as frozen_time:
        def set_timestamp(mapper, connection, target):
           now = datetime.datetime.now()
           if hasattr(target, 'created'):
               target.created = now
           if hasattr(target, 'updated'):
               target.updated = now
        event.listen(Entry, 'before_insert', set_timestamp,
                     propagate=True)
        yield frozen_time
        event.remove(Entry, 'before_insert', set_timestamp)


@pytest.fixture(scope='function')
def patch_current_time():
    return patch_time
Run Code Online (Sandbox Code Playgroud)

然后编写一个pytest类型的测试用例:

def test_patch_insert_time(patch_current_time, assertions):
    with patch_current_time("2014-06-01 16:00:00", tick=False):
        db.session.add(Entry())
        db.session.commit()
    entry = db.session.query(Entry).first()
    assert entry.created == datetime(2014, 6, 1, 16, 0, 0)
Run Code Online (Sandbox Code Playgroud)

如果有人使用unittools或其他测试包,你也可以直接使用 patch_time 函数:

class ViewTestCase(AppliactionTestCase):
    def test(self):
        with patch_time("2014-06-01 16:00:00", tick=False):
            db.session.add(Entry())
            db.session.commit()
        entry = db.session.query(Entry).first()
        self.assertEqual(entry.created, datetime(2014, 6, 1, 16, 0, 0))
Run Code Online (Sandbox Code Playgroud)

快乐编码。


Pat*_*ins 2

冷冻枪只有补丁datetime.datetime.now。您可以通过以下两种方式之一解决该问题:

  1. 用于mock.patchpatch db.func.now(),或(在我看来更简单),
  2. 使用default=datetime.datetime.now,这样 freezegun 将正确修补它。