python - Pytest - 模拟模拟的嵌套属性函数/方法的副作用

标签 python unit-testing mocking pytest

我有一个像这样模拟外部库的装置,使用 pytest-mock,它是 unittest.mock 的包装器。

# client.py

import Test as TestLibrary

class LibraryName():
   def get_client():
      return TestLibrary.Library()

# library_service.py

def using_library():
   '''
   Edited note: Library().attribute behind the scenes is set to
   self.attribute = Attribute() 
   so this may be affecting the mocking
   '''
   client = LibraryName.get_client()
   return client.attribute.method()

# conftest.py

@pytest.fixture
def library_client_mock(mocker):
    import Test as TestLibrary

    return mocker.patch.object(TestLibrary, 'Library')

# test_library_service.py

def test_library_method(library_client_mock):
   result = using_library()

我可以像这样模拟返回值:

def test_library_method(library_client_mock):
   library_client_mock.return_value.attribute.return_value.method.return_value = "test"
   result = using_library()
   assert result == "test"

但我不能用 side_effect 来模拟抛出异常

def test_library_method(library_client_mock):
    library_client_mock.return_value.attribute.return_value.method.side_effect = TypeError # doesn't work
    library_client_mock.return_value.attribute.return_value.method.side_effect = TypeError() # doesn't work
    attrs = { 'attribute.method.side_effect': TypeError }
    library_client_mock.configure_mock(**attrs) # doesn't work

    with pytest.raises(TypeError):   
        using_library() # fails assertion

我在这里缺少什么?

最佳答案

这些是您的代码中的错误:

  1. 更改:

    library_client_mock.return_value.attribute.return_value.method.return_value = "test"
    

    致:

    library_client_mock.return_value.attribute.method.return_value = "test"
    
  2. 更改:

    library_client_mock.return_value.attribute.return_value.method.side_effect = TypeError
    

    致:

    library_client_mock.return_value.attribute.method.side_effect = TypeError
    

说明

.return_value 只能用于可调用对象,例如函数为 documented :

return_value

Set this to configure the value returned by calling the mock:

>>> mock = Mock()
>>> mock.return_value = 'fish'
>>> mock()
'fish'

因此,您只能将 .return_value 用于以下用途:

  • TestLibrary.Library()
  • TestLibrary.Library().attribute.method()

但不适用于:

  • TestLibrary.Library().attribute

因为 .attribute 不是可调用的,例如TestLibrary.Library().attribute()

警告

修补Library的方式是通过其位于Test.Library(或别名为TestLibrary.Library)的源位置。具体通过:

import Test as TestLibrary
return mocker.patch.object(TestLibrary, 'Library')

它目前有效,因为您导入和使用它的方式是通过根路径。

# client.py
import Test as TestLibrary
...
    return TestLibrary.Library()
...

但是如果我们改变导入该库的方式并将本地版本导入到client.py:

# client.py
from Test import Library  # Instead of <import Test as TestLibrary>
...
    return Library()  # Instead of <TestLibrary.Library()>
...

它现在会失败。理想情况下,您应该 patch the specific name that is used by the system under test ,这里是 client.Library

import client
return mocker.patch.object(client, 'Library')

除非您确定将使用该库的所有文件将仅导入根版本而不是本地版本。

关于python - Pytest - 模拟模拟的嵌套属性函数/方法的副作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69196926/

相关文章:

python - 如何使用模拟框架模拟 Tornado 协程函数进行单元测试?

python - mock __main__

Python SimpleHTTPServer 能够注册连接尝试?

python - 不可变与可变类型

javascript - 出于测试目的,我应该避免使用有利于 e.target 的 refs 吗?

unit-testing - 基于属性的测试会让你重复代码吗?

ios - OCMock:在本地声明的对象上模拟方法

python - 为什么 -1 和 -2 在 CPython 中都散列为 -2?

python - 使用 flask-uploads 上传文件

python - 我如何模拟执行 sys.exit() 的 Python 方法 OptionParser.error()?