如何在 Flask-SqlAlchemy 中模拟 <ModelClass>.query.filter_by()
How to mock <ModelClass>.query.filter_by() in Flask-SqlAlchemy
简述
在 Flask-SqlAlchemy 中测试模型 class 时,我们如何模拟方法 .query.filter_by()
以便 return 模拟模型对象列表?
完整详情
假设我们有一个模型 class 如下代码
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class SomeModel(db.Model):
# more column mapping and methods go here
然后在我们的 Flask 代码中调用
SomeModel.query.filter_by(...)
在我们的测试代码中,使用 Python unittest model with mocking,我们想要模拟 filter_by()
调用,以便它 return 是我们设计的测试用例下的模型对象列表。
我们怎样才能做到这一点?
p.s。
我的 google 搜索只找到 ;尽管在 class 开头应用 @patch("flask_sqlalchemy.SignallingSession", autospec=True)
对我不起作用。
我还尝试模拟如下代码片段的功能
@patch('app.model.some_model.SomeModel.query.filter_by')
def test_some_case(self, filterbyMOCK):
# more test logic goes here
并且代码在启动时立即出错
RuntimeError: application not registered on db instance and no application bound to current context
来自 PyCharm IDE 的完整错误如下图所示。
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
你必须模拟整个映射器class;访问映射器上的 query
属性会导致会话加载:
@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
那是因为.query
属性是一个描述符对象;访问它会触发到会话的绑定。
另一种方法是模拟 _QueryProperty.__get__
method(支持 .query
属性);仅当您 必须 使用实际 SomeModel
实例进行测试时才使用它:
@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
演示:
>>> 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'>
只是
的总结
目标
- 我们想模拟
.query.filter_by().all()
结果
例如SomeModel.query.filter_by().all()
代码 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)
代码 02 - 与上面类似并使用 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)
简述
在 Flask-SqlAlchemy 中测试模型 class 时,我们如何模拟方法 .query.filter_by()
以便 return 模拟模型对象列表?
完整详情
假设我们有一个模型 class 如下代码
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class SomeModel(db.Model):
# more column mapping and methods go here
然后在我们的 Flask 代码中调用
SomeModel.query.filter_by(...)
在我们的测试代码中,使用 Python unittest model with mocking,我们想要模拟 filter_by()
调用,以便它 return 是我们设计的测试用例下的模型对象列表。
我们怎样才能做到这一点?
p.s。
我的 google 搜索只找到 @patch("flask_sqlalchemy.SignallingSession", autospec=True)
对我不起作用。
我还尝试模拟如下代码片段的功能
@patch('app.model.some_model.SomeModel.query.filter_by')
def test_some_case(self, filterbyMOCK):
# more test logic goes here
并且代码在启动时立即出错
RuntimeError: application not registered on db instance and no application bound to current context
来自 PyCharm IDE 的完整错误如下图所示。
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
你必须模拟整个映射器class;访问映射器上的 query
属性会导致会话加载:
@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
那是因为.query
属性是一个描述符对象;访问它会触发到会话的绑定。
另一种方法是模拟 _QueryProperty.__get__
method(支持 .query
属性);仅当您 必须 使用实际 SomeModel
实例进行测试时才使用它:
@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
演示:
>>> 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'>
只是
目标
- 我们想模拟
.query.filter_by().all()
结果 例如SomeModel.query.filter_by().all()
代码 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)
代码 02 - 与上面类似并使用 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)