python - 实例化模拟对象时,模拟属性返回值被覆盖

标签 python python-3.x mocking python-mock

背景

我正在尝试为我正在编写的应用程序设置一个测试装置,其中我的一个类被模拟替换。我很高兴将模拟类的大部分属性保留为默认值 MagicMock实例(我只对它们的用法做出断言感兴趣),但该类还有一个属性,我想为其提供特定的返回值。

作为引用,这是我试图修补的类的大纲:

class CommunicationService(object):
    def __init__(self):
        self.__received_response = Subject()

    @property
    def received_response(self):
        return self.__received_response

    def establish_communication(self, hostname: str, port: int) -> None:
        pass

    def send_request(self, request: str) -> None:
        pass

问题

我遇到的困难是当我修补 CommunicationService 时,我也尝试设置一个PropertyMockreceived_response将返回特定值的属性。然而,当我在我的生产代码中实例化这个类时,我发现对 CommunicationService.received_response 的调用正在返回默认值 MagicMock实例而不是我希望它们返回的特定值。

在测试设置期间,我执行以下操作:
context.mock_comms_exit_stack = ExitStack()
context.mock_comms = context.mock_comms_exit_stack.enter_context(
    patch('testcube.comms.CommunicationService', spec=True))

# Make 'received_response' observers subscribe to a mock subject.
context.mock_received_response_subject = Subject()
type(context.mock_comms).received_response = PropertyMock(return_value=context.mock_received_response_subject)

# Reload TestCube module to make it import the mock communications class.
reload_testcube_module(context)

在我的生产代码中(在执行此设置后调用):
# Establish communication with TestCube Web Service.
comms = CommunicationService()
comms.establish_communication(hostname, port)

# Wire plugins with communications service.
for plugin in context.command.plugins:
    plugin.on_response = comms.received_response
    plugin.request_generated.subscribe(comms.send_request)

我期待 comms.received_response成为 Subject 的一个实例(属性模拟的返回值)。但是,我得到以下信息:
<MagicMock name='CommunicationService().received_response' id='4580209944'>

问题似乎是从 patch 方法返回的实例上的模拟属性工作正常,但是在创建修补类的新实例时模拟属性会变得困惑。

南昌

我相信下面的片段捕获了这个问题的本质。如果有办法修改下面的脚本以使其print(foo.bar)返回 mock value ,然后希望它会显示我如何在我的实际代码中解决问题。
from contextlib import ExitStack
from unittest.mock import patch, PropertyMock

class Foo:
    @property
    def bar(self):
        return 'real value'

exit_stack = ExitStack()
mock_foo = exit_stack.enter_context(patch('__main__.Foo', spec=True))
mock_bar = PropertyMock(return_value='mock value')
type(mock_foo).bar = mock_bar

print(mock_foo.bar) # 'mock value' (expected)

foo = Foo()
print(foo.bar) # <MagicMock name='Foo().bar' id='4372262080'> (unexpected - should be 'mock value')

exit_stack.close()

最佳答案

以下行:

type(mock_foo).bar = mock_bar

模拟 mock_foo此时,它是 enter_context 的返回值.如果我明白 the documentation正确地意味着您现在实际上正在处理 __enter__ 的结果patch('__main__.Foo', spec=True) 的返回值.

如果您将该行更改为:
type(Foo.return_value).bar = mock_bar

那么你将模拟该属性 bar Foo 的实例数(因为调用一个类的返回值是一个实例)。然后第二个打印语句将打印 mock value正如预期的那样。

关于python - 实例化模拟对象时,模拟属性返回值被覆盖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39312530/

相关文章:

unit-testing - 使用模拟是一种好的编程习惯还是只是一种不同的方式?

python - 如何将 excel 或 csv 文件作为 Pandas 数据框上传到 Flask?

python - 在Python中使用subprocess.Popen执行shell脚本?

python - 在 PyQt 中使用对话框获取文件路径时出错

python-3.x - Python 3 中的自定义文件类型

python - 如何按行压缩两个元组列表?

c# - 使用 Request.CreateResponse 进行 ASP.NET WebApi 单元测试

python - 这个语法在 Python 中是什么意思?

python - timeit.timeit变量在python中导入

java - 在 Spock 中模拟扩展抽象类的类