Python Mock object with method called multiple times
Solution 1:
Try side_effect
def my_side_effect(*args, **kwargs):
if args[0] == 42:
return "Called with 42"
elif args[0] == 43:
return "Called with 43"
elif kwargs['foo'] == 7:
return "Foo is seven"
mockobj.mockmethod.side_effect = my_side_effect
Solution 2:
A little sweeter:
mockobj.method.side_effect = lambda x: {123: 100, 234: 10000}[x]
or for multiple arguments:
mockobj.method.side_effect = lambda *x: {(123, 234): 100, (234, 345): 10000}[x]
or with a default value:
mockobj.method.side_effect = lambda x: {123: 100, 234: 10000}.get(x, 20000)
or a combination of both:
mockobj.method.side_effect = lambda *x: {(123, 234): 100, (234, 345): 10000}.get(x, 20000)
and merrily on high we go.
Solution 3:
I've ran into this when I was doing my own testing. If you don't care about capturing calls to your methodfromdepclass() but just need it to return something, then the following may suffice:
def makeFakeMethod(mapping={}):
def fakeMethod(inputParam):
return mapping[inputParam] if inputParam in mapping else MagicMock()
return fakeMethod
mapping = {42:"Called with 42", 59:"Called with 59"}
mockobj.methodfromdepclass = makeFakeMethod(mapping)
Here's a parameterized version:
def makeFakeMethod():
def fakeMethod(param):
return "Called with " + str(param)
return fakeMethod
Solution 4:
As in here, apart from using side_effect
in unittest.mock.Mock you can also use @mock.patch.object
with new_callable
, which allows you to patch an attribute of an object with a mock object.
Let's say a module my_module.py
uses pandas
to read from a database and we would like to test this module by mocking pd.read_sql_table
method (which takes table_name
as argument).
What you can do is to create (inside your test) a db_mock
method that returns different objects depending on the argument provided:
def db_mock(**kwargs):
if kwargs['table_name'] == 'table_1':
# return some DataFrame
elif kwargs['table_name'] == 'table_2':
# return some other DataFrame
In your test function you then do:
import my_module as my_module_imported
@mock.patch.object(my_module_imported.pd, "read_sql_table", new_callable=lambda: db_mock)
def test_my_module(mock_read_sql_table):
# You can now test any methods from `my_module`, e.g. `foo` and any call this
# method does to `read_sql_table` will be mocked by `db_mock`, e.g.
ret = my_module_imported.foo(table_name='table_1')
# `ret` is some DataFrame returned by `db_mock`