I use SQLAlchemy in my Flask application. In my tests I try mocking auto time create and update for my entries. But I don't understand how it work for SQLAlchemy. For example I try mocking datetime creating with freezgun, but is failure. For example:
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))
And one more question. How forced save entry without changes, if I need refresh updated
for entry?
Freezegun only patches datetime.datetime.now
. You can solve it in one of two ways:
- Use
mock.patch
to patch db.func.now()
, or (simpler, in my opinion),
- Use
default=datetime.datetime.now
, so freezegun will patch it correctly.
sqlalchemy provide the hook sqlalchemy.event
api to patch a query session.
prerequisite: flask-sqlalchemy, pytest, freezengun, contextlib
with the example with flask-sqlalchemy which @prokoptesev used:
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())
Just write a pytest fixtures to patch the row create time:
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
Then write a pytest type test case:
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)
If somebody use unittools
or other test package, you can also use the patch_time function directly:
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))
happy coding.