【问题标题】:Is there a way to access the original function in a mocked method/function such that I can modify the arguments and pass it to the original functions?有没有办法在模拟方法/函数中访问原始函数,以便我可以修改参数并将其传递给原始函数?
【发布时间】:2015-07-31 22:37:11
【问题描述】:

我想修改传递给模块中方法的参数,而不是替换它的返回值。

我找到了解决这个问题的方法,但它似乎很有用,并且已经变成了嘲笑的教训。

模块.py

from third_party import ThirdPartyClass

ThirdPartyClass.do_something('foo', 'bar')
ThirdPartyClass.do_something('foo', 'baz')

tests.py

@mock.patch('module.ThirdPartyClass.do_something')
def test(do_something):
    # Instead of directly overriding its return value
    # I'd like to modify the arguments passed to this function.

    # change return value, no matter inputs
    do_something.return_value = 'foo'

    # change return value, based on inputs, but have no access to the original function
    do_something.side_effect = lambda x, y: y, x

    # how can I wrap do_something, so that I can modify its inputs and pass it back to the original function?
    # much like a decorator?

我尝试过类似以下的方法,但它不仅重复且丑陋,而且不起作用。经过一些 PDB 内省之后.. 我想知道这是否仅仅是因为这个第三方库的工作原理,因为当我将 pdb 放入 side_effect 时,我确实看到原始函数被成功调用。

要么是那个,要么是一些我不喜欢学习的自动嘲弄魔法。

def test():
    from third_party import ThirdPartyClass
    original_do_something = ThirdPartyClass.do_something

    with mock.patch('module.ThirdPartyClass.do_something' as mocked_do_something:
        def side_effect(arg1, arg2):
            return original_do_something(arg1, 'overridden')

        mocked_do_something.side_effect = side_effect

        # execute module.py

感谢任何指导!

【问题讨论】:

  • 你为什么说它不起作用。行为是什么? AFAIK 它应该可以工作,我用这种方式 more times... 我可以解释其他细节,但在提交答案之前,我想知道为什么 ugly 方式不能按预期工作。

标签: python mocking


【解决方案1】:

您可能希望使用参数wraps 进行模拟调用。 (Docs 供参考。)这样会调用原始函数,但它会包含 Mock 接口中的所有内容。

因此,要更改调用到原始函数的参数,您可能想这样尝试:

org.py

def func(x):
    print(x)

main.py

from unittest import mock

import org


of = org.func
def wrapped(a):
    of('--{}--'.format(a))


with mock.patch('org.func', wraps=wrapped):
    org.func('x')
    org.func.assert_called_with('x')

结果:

 --x--

【讨论】:

  • 在所有wraps相关的答案中,这个最清楚。
  • 那么如何更改参数或在返回值中添加一些东西?
  • @Velkan 在wrapped 函数中添加您需要的所有内容。在我的示例中,我通过在字符串的两侧添加 -- 来更改传递的参数。
【解决方案2】:

诀窍是将您仍想访问的原始底层函数作为参数传递给函数。

例如,对于竞争条件测试,让tempfile.mktemp 返回一个现有 路径名:

def mock_mktemp(*, orig_mktemp=tempfile.mktemp, **kwargs):
    """Ensure mktemp returns an existing pathname."""
    temp = orig_mktemp(**kwargs)
    open(temp, 'w').close()
    return temp

上面,orig_mktemp 在函数被声明时被评估,而不是在它被调用时,所以所有调用都可以通过orig_mktemp 访问tempfile.mktemp 的原始方法。 p>

我是这样使用的:

@unittest.mock.patch('tempfile.mktemp', side_effect=mock_mktemp)
def test_retry_on_existing_temp_path(self, mock_mktemp):
    # Simulate race condition: creation of temp path after tempfile.mktemp
    ...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-03
    • 2012-11-25
    • 2018-08-31
    • 2016-01-13
    • 1970-01-01
    • 1970-01-01
    • 2018-06-14
    相关资源
    最近更新 更多