当我调用 mock.patch 时,我希望它替换 type 我正在替换为我使用 new 关键字参数提供的类型。
It does not replace the type但是当 patch.start() 被调用时它确实返回了正确的对象。
FakesPatcher 是一种强制旧对象创建新对象的 hack。它适用于 Python 3.x和 PyPy . 然而,it doesn't work for Python 2.x .(请参阅下面的编辑)。
我希望 FakesPatcher 无论如何都消失并改用 mock.patch。
我在这里做错了什么,我该如何解决?
def substitute(obj, qualified_name, spec):
testdouble = mock.patch(qualified_name, spec=spec, spec_set=True, new=obj)
testdouble.attribute_name = qualified_name # Forces patch to use the extra patcher
class FakesPatcher(object):
"""Ugly hack."""
new = 1
def _new(*args, **kwargs):
return obj.__new__(obj)
def __enter__(self):
self._old_new = spec.__new__
spec.__new__ = self._new
return obj
def __exit__(self, exc_type, exc_val, exc_tb):
spec.__new__ = self._old_new
testdouble.additional_patchers.append(FakesPatcher())
return testdouble
def fake(obj):
"""
:rtype : mock._patch
:param obj:
"""
try:
configuration = obj.Configuration()
except AttributeError:
raise TypeError('A fake testdouble must have a Configuration class.')
try:
spec = configuration.spec
except AttributeError:
raise TestDoubleConfigurationError('The type to be faked was not specified.')
qualified_name = get_qualified_name(spec)
attrs = dict(obj.__dict__)
attrs.pop('Configuration')
methods = get_missing_methods(spec, obj)
for method in methods:
def make_default_implementation(attr):
def default_implementation(*args, **kwargs):
raise NotImplementedError('%s was not implemented when the object was faked.' % attr)
return default_implementation
attrs.update({method: make_default_implementation(method)})
properties = get_missing_properties(spec, obj)
for prop in properties:
def make_default_implementation(attr):
def default_implementation(*args, **kwargs):
raise NotImplementedError('%s was not implemented when the object was faked.' % attr)
return property(fget=lambda *args, **kwargs: default_implementation(*args, **kwargs),
fset=lambda *args, **kwargs: default_implementation(*args, **kwargs),
fdel=lambda *args, **kwargs: default_implementation(*args, **kwargs))
attrs.update({prop: make_default_implementation(prop)})
fake_qualified_name = get_qualified_name(obj)
obj = type(obj.__name__, obj.__bases__, attrs)
return substitute(obj, qualified_name, spec)
如果你想玩代码并测试它,你可以找到它 here .
编辑:
我 solved通过用实例方法替换 lambda 来解决 Python 2.x 错误。
请您参考如下方法:
在你的测试中,如果你想在 with 语句中使用 mock.patch,mock 库要求你使用补丁的返回值作为模拟对象。你的测试现在变成了
@it.should('replace the original methods with the fake methods')
def test_should_replace_the_original_methods_with_the_fake_methods(case):
class FakeObject(object):
class Configuration(object):
spec = RealObject
def was_faked(self):
return True
with fake(FakeObject) as realObject:
fake_obj = realObject()
case.assertTrue(fake_obj.was_faked())
然后您可以使用以下替代品,甚至摆脱它。
def substitute(obj, qualified_name, spec):
return mock.patch(qualified_name, new=obj, spec=spec)
修补通过在调用站点修补类型来工作。以下文档摘录很重要。
target should be a string in the form ‘package.module.ClassName’. The target is imported and the specified object replaced with the new object, so the target must be importable from the environment you are calling patch from. The target is imported when the decorated function is executed, not at decoration time.
如果要修补实际类型,而不使用 with 语句的返回值,则不得将类名解析为限定名,而是本地名。
以下变化
@it.should('replace the original methods with the fake methods')
def test_should_replace_the_original_methods_with_the_fake_methods(case):
...
with fake(FakeObject, '%s.%s' % (__name__,'RealObject')):
fake_obj = RealObject()
case.assertTrue(fake_obj.was_faked())
testdoubles__init__.py
def fake(obj, qualified_name=None):
"""
:rtype : mock._patch
:param obj:
"""
try:
configuration = obj.Configuration()
except AttributeError:
raise TypeError('A fake testdouble must have a Configuration class.')
try:
spec = configuration.spec
except AttributeError:
raise TestDoubleConfigurationError('The type to be faked was not specified.')
qualified_name = qualified_name or get_qualified_name(spec)
...
现在的问题是您无法可靠地找出 RealObject 的来源,至少我真的找不到方法。您可以假设它来自调用函数所在的模块并执行以下操作:
qualified_name = "%s.%s" % (obj.__module__, spec.__name__)
