When testing a model class in Flask-SqlAlchemy, how can we mock the method .query.filter_by()
so as to return the list of mocked model object
You'll have to mock the whole mapper class; accessing the query
attribute on the mapper causes a session load:
@patch('app.model.some_model.SomeModel')
def test_some_case(self, some_model_mock):
filter_by_mock = some_model_mock.query.filter_by
# more test logic goes here
That's because the .query
attribute is a descriptor object; accessing it triggers the binding to a session.
The alternative would be to mock out the _QueryProperty.__get__ method (which backs the .query
attribute); only use this if you must test with actual SomeModel
instances:
@patch('flask_sqlalchemy._QueryProperty.__get__')
def test_some_case(self, query_property_getter_mock):
filter_by_mock = query_property_getter_mock.return_value.filter_by
# more test logic goes here
Demo:
>>> from flask_sqlalchemy import SQLAlchemy
>>> db = SQLAlchemy()
>>> class SomeModel(db.Model):
... id = db.Column(db.Integer, primary_key=True)
...
>>> from unittest import mock
>>> with mock.patch('__main__.SomeModel') as model_mock:
... filter_by = model_mock.query.filter_by
... SomeModel.query.filter_by(SomeModel.id == 'foo')
...
<MagicMock name='SomeModel.query.filter_by()' id='4438980312'>
>>> with mock.patch('flask_sqlalchemy._QueryProperty.__get__') as query_property_getter_mock:
... filter_by_mock = query_property_getter_mock.return_value.filter_by
... SomeModel.query.filter_by(SomeModel.id == 'foo')
...
<MagicMock name='__get__().filter_by()' id='4439035184'>
Just a sum-up from Martijn Pieters answer
Target
.query.filter_by().all()
result
e.g. SomeModel.query.filter_by().all()
Code 01
@patch('flask_sqlalchemy._QueryProperty.__get__')
def test (
self,
queryMOCK,
):
#setup
queryMOCK\
.return_value.filter_by\
.return_value.all\
.return_value = [1,22] #empty list of current product_id
#get actual
modelObj = SomeModel.query.filter_by().all()
print(modelObj)
Code 02 - similar as above and using with
def test(self):
with patch('flask_sqlalchemy._QueryProperty.__get__') as queryMOCK #setup
queryMOCK\
.return_value.filter_by\
.return_value.all\
.return_value = [1,22] #empty list of current product_id
#get actual
modelObj = SomeModel.query.filter_by().all()
print(modelObj)