Pytest - 在 mock 的嵌套属性函数/方法上模拟 side_effect
Pytest - mocking a side_effect on mock's nested attribute function / method
我有一个像这样模拟外部库的夹具,使用 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()
我可以像这样模拟一个 return 值:
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
我在这里遗漏了什么?
这些是您的代码中的错误:
变化:
library_client_mock.return_value.attribute.return_value.method.return_value = "test"
收件人:
library_client_mock.return_value.attribute.method.return_value = "test"
变化:
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')
除非您确定将使用该库的所有文件都将只导入根目录而不是本地版本。
@Niel Godfrey Ponciano 使用 side_effect
的语法让我走上正确的道路
library_client_mock.return_value.attribute.method.side_effect = TypeError
但这还不够。
在
# conftest.py
@pytest.fixture
def library_client_mock(mocker):
import Test as TestLibrary
return mocker.patch.object(TestLibrary, 'Library')
我不得不添加一个额外的模拟:
# conftest.py
@pytest.fixture
def library_client_mock(mocker):
import Test as TestLibrary
mock_library_client = mocker.patch.object(TestLibrary, 'Library')
# option 1
mock_attribute = Mock()
# option 2, path to Library.attribute = Attribute()
mock_attribute = mocker.patch.object(TestLibrary.services, 'Attribute', autospec=True)
mock_library_client.attach_mock(mock_attribute, "attribute")
return mock_library_client
然后以下两个语句按预期工作。虽然我不确定为什么 return_value
在没有附加模拟的情况下开箱即用,但 side_effect
却没有。
# return_value set correctly
# NOTE return_value needed after each
library_client_mock.return_value.attribute.return_value.method.return_value = "test"
# side_effect set correctly
# NOTE return_value not needed after "attribute"
library_client_mock.return_value.attribute.method.side_effect = TypeError
我有一个像这样模拟外部库的夹具,使用 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()
我可以像这样模拟一个 return 值:
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
我在这里遗漏了什么?
这些是您的代码中的错误:
变化:
library_client_mock.return_value.attribute.return_value.method.return_value = "test"
收件人:
library_client_mock.return_value.attribute.method.return_value = "test"
变化:
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')
除非您确定将使用该库的所有文件都将只导入根目录而不是本地版本。
@Niel Godfrey Ponciano 使用 side_effect
library_client_mock.return_value.attribute.method.side_effect = TypeError
但这还不够。
在
# conftest.py
@pytest.fixture
def library_client_mock(mocker):
import Test as TestLibrary
return mocker.patch.object(TestLibrary, 'Library')
我不得不添加一个额外的模拟:
# conftest.py
@pytest.fixture
def library_client_mock(mocker):
import Test as TestLibrary
mock_library_client = mocker.patch.object(TestLibrary, 'Library')
# option 1
mock_attribute = Mock()
# option 2, path to Library.attribute = Attribute()
mock_attribute = mocker.patch.object(TestLibrary.services, 'Attribute', autospec=True)
mock_library_client.attach_mock(mock_attribute, "attribute")
return mock_library_client
然后以下两个语句按预期工作。虽然我不确定为什么 return_value
在没有附加模拟的情况下开箱即用,但 side_effect
却没有。
# return_value set correctly
# NOTE return_value needed after each
library_client_mock.return_value.attribute.return_value.method.return_value = "test"
# side_effect set correctly
# NOTE return_value not needed after "attribute"
library_client_mock.return_value.attribute.method.side_effect = TypeError