How to mock <ModelClass>.query.filter_by() in Flask-SqlAlchemy
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
- We want to mock
.query.filter_by().all()
resulte.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] #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] #get actual modelObj = SomeModel.query.filter_by().all() print(modelObj)