In brief
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 objects?
Full details
Let's say we have a model class as below code
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class SomeModel(db.Model):
# more column mapping and methods go here
Then in our Flask code we call
SomeModel.query.filter_by(...)
In our testing code, using Python unittest model with mocking, we want to mock the filter_by()
call so that it returns a list of model objects under our designed test case.
How can we get to that?
p.s.
My google search only found this related post; though applying @patch("flask_sqlalchemy.SignallingSession", autospec=True)
at the beginning of the class not work for me.
I also tried to mock the function as below code snippet
@patch('app.model.some_model.SomeModel.query.filter_by')
def test_some_case(self, filterbyMOCK):
# more test logic goes here
and the code get immediate error when started
RuntimeError: application not registered on db instance and no application bound to current context
The full error from PyCharm IDE as snapshot below.
Traceback (most recent call last):
File "/home/namgivu/NN/code/someproject-cloud/venv/local/lib/python2.7/site-packages/mock/mock.py", line 1297, in patched
arg = patching.__enter__()
File "/home/namgivu/NN/code/someproject-cloud/venv/local/lib/python2.7/site-packages/mock/mock.py", line 1353, in __enter__
self.target = self.getter()
File "/home/namgivu/NN/code/someproject-cloud/venv/local/lib/python2.7/site-packages/mock/mock.py", line 1523, in <lambda>
getter = lambda: _importer(target)
File "/home/namgivu/NN/code/someproject-cloud/venv/local/lib/python2.7/site-packages/mock/mock.py", line 1210, in _importer
thing = _dot_lookup(thing, comp, import_path)
File "/home/namgivu/NN/code/someproject-cloud/venv/local/lib/python2.7/site-packages/mock/mock.py", line 1197, in _dot_lookup
return getattr(thing, comp)
File "/home/namgivu/NN/code/someproject-cloud/venv/local/lib/python2.7/site-packages/flask_sqlalchemy/__init__.py", line 428, in __get__
return type.query_class(mapper, session=self.sa.session())
File "/home/namgivu/NN/code/someproject-cloud/venv/local/lib/python2.7/site-packages/sqlalchemy/orm/scoping.py", line 78, in __call__
return self.registry()
File "/home/namgivu/NN/code/someproject-cloud/venv/local/lib/python2.7/site-packages/sqlalchemy/util/_collections.py", line 990, in __call__
return self.registry.setdefault(key, self.createfunc())
File "/home/namgivu/NN/code/someproject-cloud/venv/local/lib/python2.7/site-packages/flask_sqlalchemy/__init__.py", line 136, in __init__
self.app = db.get_app()
File "/home/namgivu/NN/code/someproject-cloud/venv/local/lib/python2.7/site-packages/flask_sqlalchemy/__init__.py", line 809, in get_app
raise RuntimeError('application not registered on db '
RuntimeError: application not registered on db instance and no application bound to current context
You'll have to mock the whole mapper class; accessing the
query
attribute on the mapper causes a session load: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 actualSomeModel
instances:Demo:
Just a sum-up from Martijn Pieters answer
Target
.query.filter_by().all()
result e.g.SomeModel.query.filter_by().all()
Code 01
Code 02 - similar as above and using
with