我正在尝试为我正在编写的应用程序设置一个测试夹具,其中一个类被替换为mock。我很高兴将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
时,我还尝试为PropertyMock
属性设置received_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
的实例(属性mock的返回值)。但是,我得到以下内容:
<MagicMock name='CommunicationService().received_response' id='4580209944'>
问题似乎是从补丁方法返回的实例上的mock属性工作正常,但是在创建补丁类的新实例时, mock属性会搞乱。
我相信下面的代码段可以捕捉到这个问题的本质。如果有办法修改下面的脚本以使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()
答案 0 :(得分:2)
以下一行:
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
。
答案 1 :(得分:0)
这不是问题的答案,而是我从 Simeon 的回答中学到的对更简单问题的解决方案。
就我而言,我想模拟 obj.my_method().my_property
并获得 PropertyMock
作为回报,因为我将返回值的属性直接设置为 PropertyMock
实例,而不是 {{ 1}} 的方法返回的模拟。这是固定代码:
type