Python - 模拟链式函数调用
Python - Mocking chained function calls
我在单元测试的其中一个方法中有如下语句。
db_employees = self.db._session.query(Employee).filter(Employee.dept ==
new_employee.dept).all()
我想要 db_employees 获取员工的模拟列表。我尝试使用以下方法实现此目的:
m = MagickMock()
m.return_value.filter().all().return_value = employees
其中员工是员工对象的列表。但这没有用。当我尝试打印任何属性的值时,它都有一个模拟值。这是代码的样子:
class Database(object):
def __init__(self, user=None, passwd=None, db="sqlite:////tmp/emp.db"):
try:
engine = create_engine(db)
except Exception:
raise ValueError("Database '%s' does not exist." % db)
def on_connect(conn, record):
conn.execute('pragma foreign_keys=ON')
if 'sqlite://' in db:
event.listen(engine, 'connect', on_connect)
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
self._session = DBSession()
class TestEmployee(MyEmployee):
def setUp(self):
self.db = emp.database.Database(db=options.connection)
self.db._session._autoflush()
@mock.patch.object(session.Session, 'add')
@mock.patch.object(session.Session, 'query')
def test_update(self, mock_query, mock_add):
employees = [{'id': 1,
'name': 'Pradeep',
'department': 'IT',
'manager': 'John'}]
mock_add.side_effect = self.add_side_effect
mock_query.return_value = self.query_results()
self.update_employees(employees)
def add_side_effect(self, instance, _warn=True):
// Code to mock add
// Values will be stored in a dict which will be used to
// check with expected value.
def query_results(self):
m = MagicMock()
if self.count == 0:
m.return_value.filter.return_value.all.return_value = [employee]
elif:
m.return_value.filter.return_value.all.return_value = [department]
return m
我有 query_results 因为被测方法调用查询两次。首先是员工 table,然后是部门 table。
如何模拟这个链式函数调用?
您应该修补 _session
的 Database
属性的 query()
方法并对其进行配置以提供正确的答案。您可以通过多种方式完成此操作,但恕我直言,更简洁的方法是修补 DBSession
的 query
静态引用。我不知道你从 模块导入 DBSession
所以我会修补本地参考。
另一方面是模拟配置:我们将设置 query
的 return 值,在您的情况下,该值成为具有 filter()
方法的对象。
class TestEmployee(MyEmployee):
def setUp(self):
self.db = emp.database.Database(db=options.connection)
self.db._session._autoflush()
self.log_add = {}
@mock.patch.object(__name__.'DBSession.add')
@mock.patch.object(__name__.'DBSession.query')
def test_update(self, mock_query, mock_add):
employees = [{'id': 1,
'name': 'Pradeep',
'department': 'IT',
'manager': 'John'}]
mock_add.side_effect = self.add_side_effect
mock_query.return_value = self.query_results()
self.update_employees(employees)
.... your test here
def add_side_effect(self, instance, _warn=True):
# ... storing data
self.log_add[...] = [...]
def query_results(self):
m = MagicMock()
value = "[department]"
if not self.count:
value = "[employee]"
m.filter.return_value.all.return_value = value
return m
m = Mock()
m.session.query().filter().all.return_value = employees
我找到了一个类似问题的解决方案,我需要模拟一组嵌套的过滤调用。
给定的测试代码类似于以下内容:
interesting_cats = (session.query(Cats)
.filter(Cat.fur_type == 'furry')
.filter(Cat.voice == 'meowrific')
.filter(Cat.color == 'orande')
.all())
您可以像下面这样设置模拟:
mock_session_response = MagicMock()
# This is the magic - create a mock loop
mock_session_response.filter.return_value = mock_session_response
# We can exit the loop with a call to 'all'
mock_session_response.all.return_value = provided_cats
mock_session = MagicMock(spec=Session)
mock_session.query.return_value = mock_session_response
我在单元测试的其中一个方法中有如下语句。
db_employees = self.db._session.query(Employee).filter(Employee.dept ==
new_employee.dept).all()
我想要 db_employees 获取员工的模拟列表。我尝试使用以下方法实现此目的:
m = MagickMock()
m.return_value.filter().all().return_value = employees
其中员工是员工对象的列表。但这没有用。当我尝试打印任何属性的值时,它都有一个模拟值。这是代码的样子:
class Database(object):
def __init__(self, user=None, passwd=None, db="sqlite:////tmp/emp.db"):
try:
engine = create_engine(db)
except Exception:
raise ValueError("Database '%s' does not exist." % db)
def on_connect(conn, record):
conn.execute('pragma foreign_keys=ON')
if 'sqlite://' in db:
event.listen(engine, 'connect', on_connect)
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
self._session = DBSession()
class TestEmployee(MyEmployee):
def setUp(self):
self.db = emp.database.Database(db=options.connection)
self.db._session._autoflush()
@mock.patch.object(session.Session, 'add')
@mock.patch.object(session.Session, 'query')
def test_update(self, mock_query, mock_add):
employees = [{'id': 1,
'name': 'Pradeep',
'department': 'IT',
'manager': 'John'}]
mock_add.side_effect = self.add_side_effect
mock_query.return_value = self.query_results()
self.update_employees(employees)
def add_side_effect(self, instance, _warn=True):
// Code to mock add
// Values will be stored in a dict which will be used to
// check with expected value.
def query_results(self):
m = MagicMock()
if self.count == 0:
m.return_value.filter.return_value.all.return_value = [employee]
elif:
m.return_value.filter.return_value.all.return_value = [department]
return m
我有 query_results 因为被测方法调用查询两次。首先是员工 table,然后是部门 table。
如何模拟这个链式函数调用?
您应该修补 _session
的 Database
属性的 query()
方法并对其进行配置以提供正确的答案。您可以通过多种方式完成此操作,但恕我直言,更简洁的方法是修补 DBSession
的 query
静态引用。我不知道你从 模块导入 DBSession
所以我会修补本地参考。
另一方面是模拟配置:我们将设置 query
的 return 值,在您的情况下,该值成为具有 filter()
方法的对象。
class TestEmployee(MyEmployee):
def setUp(self):
self.db = emp.database.Database(db=options.connection)
self.db._session._autoflush()
self.log_add = {}
@mock.patch.object(__name__.'DBSession.add')
@mock.patch.object(__name__.'DBSession.query')
def test_update(self, mock_query, mock_add):
employees = [{'id': 1,
'name': 'Pradeep',
'department': 'IT',
'manager': 'John'}]
mock_add.side_effect = self.add_side_effect
mock_query.return_value = self.query_results()
self.update_employees(employees)
.... your test here
def add_side_effect(self, instance, _warn=True):
# ... storing data
self.log_add[...] = [...]
def query_results(self):
m = MagicMock()
value = "[department]"
if not self.count:
value = "[employee]"
m.filter.return_value.all.return_value = value
return m
m = Mock()
m.session.query().filter().all.return_value = employees
我找到了一个类似问题的解决方案,我需要模拟一组嵌套的过滤调用。
给定的测试代码类似于以下内容:
interesting_cats = (session.query(Cats)
.filter(Cat.fur_type == 'furry')
.filter(Cat.voice == 'meowrific')
.filter(Cat.color == 'orande')
.all())
您可以像下面这样设置模拟:
mock_session_response = MagicMock()
# This is the magic - create a mock loop
mock_session_response.filter.return_value = mock_session_response
# We can exit the loop with a call to 'all'
mock_session_response.all.return_value = provided_cats
mock_session = MagicMock(spec=Session)
mock_session.query.return_value = mock_session_response