【问题标题】:How to mocking created time in sqlalchemy?如何在 sqlalchemy 中模拟创建时间?
【发布时间】:2015-03-18 07:55:06
【问题描述】:

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

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))

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

【问题讨论】:

    标签: python unit-testing testing sqlalchemy mocking


    【解决方案1】:

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

    先决条件:flask-sqlalchemy、pytest、frozengun、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())
    

    只需编写一个 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
    

    然后写一个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)
    

    如果有人使用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))
    

    编码愉快。

    【讨论】:

      【解决方案2】:

      Freezegun 仅修补 datetime.datetime.now。您可以通过以下两种方式之一来解决它:

      1. 使用mock.patch 修补db.func.now(),或者(在我看来更简单),
      2. 使用default=datetime.datetime.now,因此 freezegun 会正确修补它。

      【讨论】:

      • 这对我不起作用。我不知道它是如何工作的,但如果我在模型中使用 default=datetime.datetime.now,则日期时间已修补,但在保存的模型中 created 返回当前时间。
      • 如何修补db.func.now?如我错了请纠正我。 db.func.now 是保存模型时的数据库函数。可能是补丁不正确?
      • Entry 类相对于您的测试在哪里?
      • 对不起,我不明白你的意思。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-07-17
      • 2017-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多