【问题标题】:Mocking function argument names to work with inspect.getargspec模拟函数参数名称以使用 inspect.getargspec
【发布时间】:2015-10-29 18:09:42
【问题描述】:

我正在尝试模拟一个测试函数,该测试最终通过inspect.getargspec 函数传递模拟。我想使用一些特定的名称作为模拟函数的参数名称。

据我所知,没有具体的方法来模拟参数名称,所以我尝试使用 Mock 的 spec= 参数:

In [1]: import mock, inspect

In [2]: inspect.getargspec(mock.Mock(spec=lambda a,b:None))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-de358c5a968d> in <module>()
----> 1 inspect.getargspec(mock.Mock(spec=lambda a,b:None))

/usr/lib/python2.7/inspect.pyc in getargspec(func)
    815     if not isfunction(func):
    816         raise TypeError('{!r} is not a Python function'.format(func))
--> 817     args, varargs, varkw = getargs(func.func_code)
    818     return ArgSpec(args, varargs, varkw, func.func_defaults)
    819 

/usr/lib/python2.7/inspect.pyc in getargs(co)
    750 
    751     if not iscode(co):
--> 752         raise TypeError('{!r} is not a code object'.format(co))
    753 
    754     nargs = co.co_argcount

TypeError: <Mock name='mock.func_code' id='140395908951120'> is not a code object

所以,是的,确实,Mock 不是代码对象。如何模拟参数名称以便 inspect.getargspec 返回这些名称?

【问题讨论】:

  • Mock 对象的一点是,创建它通常比编写适当的模拟类或模拟函数更容易。如果Mock 类不能为您简化事情,只需编写一个模拟函数即可。 (我知道你不会免费获得函数调用跟踪功能,但也许你甚至不需要它。)
  • @SvenMarnach:是的,实际上我想使用 Mock 因为呼叫跟踪。但也许你是对的……
  • 你可以在你的模拟函数中调用一个模拟来进行跟踪。 :)
  • @SvenMarnach:你能把这两个都放在一个答案中,这样我就可以正确地给你投票了吗?
  • 我已经在正确的答案中详细阐述了 cmets。

标签: python python-2.7 inspect python-mock


【解决方案1】:

您可以改为修补 inspect.getargspec

getargspec_orig = inspect.getargspec
def getargspec_patch(f):
    if f == <your function>:
        return ArgSpec(args=[...], varargs=..., keywords=..., defaults=(...))
    else:
        return getargspec_orig(f)

with patch('inspect.getargspec', new_callable=getargspec_patch):
    # do your testing here

【讨论】:

    【解决方案2】:

    一个简单的解决方案是使用适当的模拟函数而不是 mock.Mock 实例。如果您需要 Mock 实例的调用跟踪和 Mock 实例的隐式创建作为返回值,您可以简单地从您的模拟函数中调用这样的实例:

    def test_something(self):
        mock_instance = mock.Mock()
        def mock_callback(a, b):
            return mock_instance(a, b)
        result = function_to_test(mock_callback)
        self.assertEqual(mock_instance.call_count, 3)
    

    如果您需要多个函数中的回调(或出于其他原因不希望它成为本地函数),您可以编写一个工厂函数来返回回调和 Mock 实例:

    def make_mock_callback():
        mock_instance = mock.Mock()
        def mock_callback(a, b):
            return mock_instance(a, b)
        return mock_instance, mock_callback
    

    通过关闭mock_instance,您可以确保每个回调都有自己的Mock,而不会污染函数的原型。

    【讨论】:

      【解决方案3】:

      设法找到几乎不会造成伤害的解决方法。因此,按照问题文本中的示例:

      In [1]: import mock, inspect
      

      定义规范函数(分配给一个标识符,因为我以后需要它两次):

      In [2]: specfun = lambda a,b: None
      

      定义模拟。在做了一些实验后,我注意到inspect.getargspec 只需要一个func_code 对象:

      In [3]: mockfun = mock.Mock(spec=specfun, func_code=specfun.func_code)
      

      它是否适用于inspect.getargspec?是的!

      In [4]: inspect.getargspec(mockfun)
      Out[4]: ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=<Mock name='mock.func_defaults' id='140160757665168'>)
      

      它是否像普通的可调用 Mock 一样工作?是的!

      In [5]: mockfun('hello', 42)
      Out[5]: <Mock name='mock()' id='140160757483984'>
      
      In [6]: mockfun.call_args_list
      Out[6]: [call('hello', 42)]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-09-21
        • 2022-08-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-18
        • 2017-12-23
        相关资源
        最近更新 更多