我们在python中使用Mock已经有一段时间了。

现在,我们有这样一种情况,我们想模拟一个函数

def foo(self, my_param):
    #do something here, assign something to my_result
    return my_result

通常,模拟的方法是(假设foo是对象的一部分)

self.foo = MagicMock(return_value="mocked!")

甚至,如果我调用foo()几次,我可以使用

self.foo = MagicMock(side_effect=["mocked once", "mocked twice!"])

现在,我面临这样一种情况:当输入参数具有特定值时,我想返回一个固定值。如果my_param等于something那么我要返回my_cool_mock

这似乎在python的mockito上可用

when(dummy).foo("something").thenReturn("my_cool_mock")

我一直在寻找如何实现同样的Mock没有成功?

什么好主意吗?


当前回答

如果side_effect_func是一个函数,则该函数返回的是 什么调用模拟返回。调用side_effect_func函数 和模拟的论点一样。这允许你改变回报 基于输入动态调用的值: >>>定义side_effect_func(value): …返回值+ 1 ... >>> m = MagicMock(side_effect=side_effect_func) > > > m (1) 2 > > > m (2) 3. > > > m.mock_calls ((1)打电话,电话(2))

http://www.voidspace.org.uk/python/mock/mock.html#calling

其他回答

为了展示另一种方法:

def mock_isdir(path):
    return path in ['/var/log', '/var/log/apache2', '/var/log/tomcat']

with mock.patch('os.path.isdir') as os_path_isdir:
    os_path_isdir.side_effect = mock_isdir

如果你“想在输入参数有特定值时返回一个固定值”,也许你甚至不需要mock,可以使用dict及其get方法:

foo = {'input1': 'value1', 'input2': 'value2'}.get

foo('input1')  # value1
foo('input2')  # value2

当fake的输出是输入的映射时,这种方法很有效。当它是输入的函数时,我建议根据Amber的回答使用side_effect。

你也可以使用两者的组合,如果你想保留Mock的功能(assert_called_once, call_count等):

self.mock.side_effect = {'input1': 'value1', 'input2': 'value2'}.get

如果side_effect_func是一个函数,则该函数返回的是 什么调用模拟返回。调用side_effect_func函数 和模拟的论点一样。这允许你改变回报 基于输入动态调用的值: >>>定义side_effect_func(value): …返回值+ 1 ... >>> m = MagicMock(side_effect=side_effect_func) > > > m (1) 2 > > > m (2) 3. > > > m.mock_calls ((1)打电话,电话(2))

http://www.voidspace.org.uk/python/mock/mock.html#calling

你也可以使用@mock.patch.object:

假设my_module.py模块使用pandas从数据库中读取数据,我们想通过模拟pd来测试这个模块。Read_sql_table方法(以table_name作为参数)。

你能做的是(在你的测试中)创建一个db_mock方法,根据提供的参数返回不同的对象:

def db_mock(**kwargs):
    if kwargs['table_name'] == 'table_1':
        # return some DataFrame
    elif kwargs['table_name'] == 'table_2':
        # return some other DataFrame

在你的测试函数中,你可以这样做:

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`

如在多次调用方法的Python Mock对象中所述

解决办法是写我自己的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

这就成功了