【问题标题】:Python Patch/Mock class method but still call original methodPython Patch/Mock 类方法,但仍调用原始方法
【发布时间】:2021-07-23 10:06:18
【问题描述】:

我想使用 patch 记录对单元测试类中的函数所做的所有函数调用,但需要原始函数仍按预期运行。我在下面创建了一个虚拟代码示例:

from mock import patch

class A(object):
    def __init__(self):
        self._a = 1

class B(A):
    def __init__(self):
        super(B, self).__init__() # TypeError: super() argument 1 must be type, not MagicMock
        self._b = 11

    def bar(self, b):
        self._b = self._b + 1 + b

    def foo(self, b):
        self.bar(b)

class MockB(B):
    def foo(self, b):
        super(MockB, self).foo(self, b)

@patch('main.B')
def main(b_class):
    b_class.side_effect = MockB

    b = B()

    print b._b # 11
    b.foo(0)
    print b._b # 12

main()

就我而言,b = B() 类的实例实际上不在主函数中,而是在另一个模块中,因此我无法模拟该实例。我一般需要它作为 B 的所有实例的装饰器。

总结:我不确定如何单独模拟类方法,但仍然调用原始方法。之后,我想使用call_args_list 之类的东西,在那里我可以看到对foo() 的所有调用。

【问题讨论】:

  • 你想在这里实现什么,实际测试的是什么?
  • @jonrsharpe 谢谢它不相关。我已经删除了标签。
  • 什么不相关?如果你不测试,你为什么要嘲笑任何东西?
  • 相关部分是我需要模拟一个类方法并保持原始方法工作。我想模拟,因为它带有 call_args_list 和其他有用的功能。

标签: python mocking


【解决方案1】:

我认为您正在寻找 wraps Mock 参数。在官方documentation 中搜索wraps。访问属性会返回一个模拟对象。如果没有为 mock 配置返回值,则调用方法会给出真实的方法结果。

【讨论】:

    【解决方案2】:

    更多详情可以在这里找到:https://stackoverflow.com/a/61963740/1731460

    import mock
    from contextlib import contextmanager
    
    
    @contextmanager
    def mock_patch_method_original(mock_path, original_method, results):
        results = iter(results)
    
        def side_effect(self, *args, **kwargs):
            value = next(results)
            if value == '__original__':
                side_effect.self = self
                return original_method(self, *args, **kwargs)
            else:
                return value
    
        patcher = mock.patch(mock_path, autospec=True, side_effect=side_effect)
        yield patcher.start()
        patcher.stop()
    

    【讨论】:

      【解决方案3】:

      只需保留对原始、非模拟/修补实例的引用即可。

      我在修补ctypes.windll.winmm.mciSendStringW 时遇到了问题,但我仍然希望能够访问该原始功能。最初,我尝试过:

      from ctypes import windll
      originalWindll = windll
      
      # Later on trying to call the original mciSendStringW when it's patched...
      originalWindll.winmm.mciSendStringW(...)  # Didn't work! Called the patched version of the function!
      

      正确的做法实际上是这样......因为它是被模拟/修补的函数,所以它是我需要保留引用的函数。

      from ctypes import windll
      originalMCISendStringW = windll.winmm.mciSendStringW
      
      # Later on trying to call the original mciSendStringW when it's patched...
      originalMCISendStringW(...)  # Works! Calls the original, non-patched version of this function!
      

      【讨论】:

        猜你喜欢
        • 2014-10-18
        • 1970-01-01
        • 1970-01-01
        • 2021-06-01
        • 1970-01-01
        • 2013-01-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多