模拟文件中导入的类的 __init__

vij*_*ker 2 python unit-testing mocking

我有一个文件 copy_x_to_y.py ,如下所示:

from abcd import F

def function_to_be_tested():
     F()
Run Code Online (Sandbox Code Playgroud)

在 abcd.py 文件中,我有这样的内容:

from xyz import XY

class F():
   def __init__(self, arg1):
       self.xy = XY(arg1)
Run Code Online (Sandbox Code Playgroud)

我想在我的测试用例中模拟 XY 的init 。我尝试用以下方法来模拟 F 的 init:

def mock_func(*args, **kwargs):
    pass

@patch('path/to/copy_x_to_y.F.__init__', mock_func)
def test():
    assert function_to_be_tested() is None
Run Code Online (Sandbox Code Playgroud)

但它总是会调用 XY 的 init,导致错误,因为它的初始化调用通过 arg1 与 S3 连接。如何测试这种结构?

小智 5

__init__想要嘲笑的理由是什么XY?您是否希望它返回 XY 的特定对象,是否想检查是否XY.__init__使用特定参数或其他内容调用?

解决您的问题的一个可能的解决方案是模拟整个类,但让它返回一个“正常”对象。这是一个例子:

>>> from unittest.mock import patch
>>> class MyClass:
...   def __init__(self, val):
...     self._val = val
...   def foo(self):
...     print(self._val)
... 
>>> a = MyClass(1)
>>> a.foo()
1
>>> patcher = patch('__main__.MyClass', return_value=a)
>>> mock_class = patcher.start()
>>> b = MyClass(2)  # This will return a.
>>> b.foo()
1
>>> mock_class.call_args_list
[call(2)]
>>> patcher.stop()
Run Code Online (Sandbox Code Playgroud)

在你的情况下是:

from xyz import XY
from path/to/copy_x_to_y import function_to_be_tested
def test():
  arg1 = ...
  a = XY(arg1)  # Has to be called before the patch to get a "normal" object.
  with patch('xyz.XY', return_value=a) as mock_xy:
    # Run funcion to be tested here and check results.
    function_to_be_tested()
    assert ...
Run Code Online (Sandbox Code Playgroud)

一些旁注:

  1. __init__如果您确实需要这样做,则可以直接模拟。
>>> def my_init(self, *args, **kwargs):
...   self._val = 1
>>> patcher = patch.object(MyClass, '__init__', my_init)
>>> mock_init = patcher.start()
>>> a = MyClass(2)
>>> a.foo()
1
Run Code Online (Sandbox Code Playgroud)
  1. 如果您使用 patch 装饰器,则必须为装饰函数提供一个额外的参数,该参数是类或对象的模拟。 https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch
@patch('path/to/SomeClass', ...)
def test(mock_class):
...
Run Code Online (Sandbox Code Playgroud)
  1. 另外 patch 通常(唯一?)用于修补类,而 patch.object 用于修补类或模块内的成员。https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch.object